Wie kann die Anzahl der Dateien in einem Verzeichnis am besten gezählt werden?

11

Wenn das Parsen der Ausgabe von lsgefährlich ist, weil einige funky Zeichen (Leerzeichen ,, \n...) beschädigt werden können, wie lässt sich die Anzahl der Dateien in einem Verzeichnis am besten ermitteln?

Ich verlasse mich normalerweise darauf find, dieses Parsen zu vermeiden, find mydir | wc -lwerde aber aus den gleichen Gründen ebenfalls brechen.

Ich arbeite gerade an Solaris, suche aber nach einer Antwort, die für verschiedene Unices und Shells so portabel wie möglich ist.

Rahmu
quelle
3
Ich bin mir nicht sicher, ob es ein Duplikat ist. Vermisse ich etwas?
Rahm
1
Dies könnte ein Duplikat sein, aber nicht die angegebene Frage. finderhalten Sie die Anzahl der Dateien rekursiv (verwenden -maxdepth 1Sie, wenn Sie das nicht möchten. find mydir -maxdepth 1 -type f -printf \\n | wc -lsollten die Sonderzeichen im Dateinamen behandeln, da sie überhaupt nicht gedruckt werden.
Anthon

Antworten:

15

Wie wäre es mit diesem Trick?

find . -maxdepth 1 -exec echo \; | wc -l

So tragbar wie findund wc.

rozcietrzewiacz
quelle
5
Dies funktioniert nicht (es werden n+1Dateien auf meinem Debian-System angezeigt ). Es wird auch nicht nach regulären Dateien gefiltert.
Chris Down
4
Ich habe nur ein allgemeines Beispiel gegeben. Es funktioniert, aber wie es funktioniert, hängt davon ab, wie Sie den findBefehl an Ihre spezifischen Anforderungen anpassen . Ja, dieses enthält alle Verzeichnisse, einschließlich .(weshalb Sie das Ergebnis möglicherweise als sehen n+1).
Rozcietrzewiacz
Ich mag diesen Trick, sehr klug; aber ich bin überrascht, dass es keinen einfachen Weg gibt, das zu tun!
Rahm
3
@ChrisDown Das OP gibt keine Filterung für reguläre Dateien an und fragt nach der Anzahl der Dateien in einem Verzeichnis. Verwenden Sie find . -maxdepth 1 ! -name . -exec echo \; | wc -l; Einige ältere Versionen von findhaben nicht -not.
Arcege
3
Beachten Sie, dass dies -maxdepthkein Standard ist (eine GNU-Erweiterung, die jetzt auch von einigen anderen Implementierungen unterstützt wird).
Stéphane Chazelas
11

Mit Bash, ohne externe Dienstprogramme oder Schleifen:

shopt -s dotglob
files=(*)
echo ${#files[@]}

In ksh- ersetzen shopt -s dotglobdurch FIGNORE=.?(.). Ersetzen Sie es in zsh durch setopt glob_dotsoder entfernen Sie den shoptAnruf und verwenden Sie files=(*(D)). (Oder lassen Sie einfach die Zeile fallen, wenn Sie keine Punktdateien einfügen möchten.) Portabel, wenn Sie sich nicht für Punktdateien interessieren:

set -- *
echo $#

Wenn Sie Punktdateien einschließen möchten:

set -- *
if [ -e "$1" ]; then c=$#; else c=0; fi
set .[!.]*
if [ -e "$1" ]; then c=$((c+$#)); fi
set ..?*
if [ -e "$1" ]; then c=$((c+$#)); fi
echo $c
Enzotib
quelle
2
Das erste Beispiel druckt 1für ein leeres Verzeichnis, wenn nullglobes nicht aktiviert ist. In zsh führt a=(*(DN));echo ${#a}das Qualifikationsmerkmal N( nullglob) nicht zu einem Fehler für ein leeres Verzeichnis.
Nisetama
8
find . ! -name . -prune -print | grep -c /

Sollte für Post-80-Systeme ziemlich portabel sein.

Das zählt alle Verzeichniseinträge außer .und ..im aktuellen Verzeichnis.

So zählen Sie auch Dateien in Unterverzeichnissen:

find .//. ! -name . | grep -c //

(dass man auch auf Unix V6 (1975) portabel sein sollte, da es nicht benötigt wird -prune)

Stéphane Chazelas
quelle
Eine der seltenen tragbaren Antworten auf dieser Seite, wenn nicht die einzige.
Xhienne
Ich habe diese Antwort gestern positiv bewertet, da ich festgestellt habe, dass sie auch für andere Verzeichnisse als das aktuelle Verzeichnis ( find dirname ! -name dirname -prune -print) gut funktioniert . Ich habe mich seitdem gefragt, ob es einen bestimmten Grund gibt, grep -c /anstatt zu verwenden wc -l(was wahrscheinlich häufiger zum Zählen verwendet wird).
Anthony Geoghegan
1
find dirname ! -name dirnamefunktioniert nicht, wenn andere Verzeichnisse mit diesem Namen vorhanden sind dirname. Es ist besser zu benutzen find dirname/. ! -name .. wc -lzählt die Anzahl der Zeilen, Dateinamen können aus mehreren Zeilen bestehen, da das Zeilenumbruchzeichen genauso gültig ist wie jedes in einem Dateinamen.
Stéphane Chazelas
6

Versuchen:

ls -b1A | wc -l

Das -bhat nicht druckbare Zeichen, -Azeigt alle Dateien außer .und ..und eine pro Zeile (die Standardeinstellung in einer Pipe, aber gut, um explizit zu sein).

Solange wir übergeordnete Skriptsprachen einschließen, ist hier ein Einzeiler in Python:

python -c 'import os; print len(os.listdir(os.sep))'

Oder mit vollem "Fund":

python -c 'import os; print len([j for i in os.walk(os.sep) for j in i[1]+i[2]])'
Arcege
quelle
1

Yoc kann eine solche Konstruktion verwenden:

I=0; for i in * ; do ((I++)); done ; echo $I

Aber ich fürchte, Sie können Fehler verursachen, Argument list too long.wenn Sie zu viele Dateien im Verzeichnis haben. Ich habe es jedoch in einem Verzeichnis mit 10 Milliarden Dateien getestet und es hat gut funktioniert.

eilen
quelle
3
Dies funktioniert auch nicht für versteckte Dateien, es sei denn, die Shell ist so konfiguriert, dass sie diese mit erweitert *.
Lekensteyn
gnu find . -maxdepth 1 -type f | wc -l
Nikhil Mulley
4
@Rush: Dieser Befehl sollte niemals "arg list too long" auslösen. Das passiert nur mit externem Befehl (also niemals mit for.
Enzotib
1

Haben Sie über Perl nachgedacht, das relativ portabel sein sollte?

Etwas wie:

use File::Find;

$counter = 0;

sub wanted { 
  -f && ++$counter
}

find(\&wanted, @directories_to_search);
print "$counter\n";
cjc
quelle
0

Versuchen Sie dies => Verwenden von ls mit den Optionen -i (für Knotennummer) & -F (Anhängt den Verzeichnisnamen mit '/').

ls -ilF | egrep -v '/' | wc -l
Saumil
quelle
0

Mit einem perlEinzeiler (zur besseren Lesbarkeit neu formatiert):

perl -e 'opendir($dh, ".");
         while ( readdir($dh) ) {$count++};
         closedir $dh;
         print "$count\n";'

oder

perl -e 'opendir($dh, ".");
         @files = readdir($dh);
         closedir $dh;
         print $#files+1,"\n";'

Sie können perlFunktionen verwenden, die Arrays wie grepoder mapmit der zweiten Version ändern . Siehe perldoc -f readdirfür ein Beispiel mit grep.

cas
quelle
0

Die einfachste Version, die ich ständig benutze und mit der ich nie Probleme hatte, ist: ls -b1 | wc -l

Peter
quelle
Sie könnten auf Probleme \nstoßen, wenn der Dateiname ein oder andere funky Zeichen enthält (ja, bestimmte Unices erlauben dies).
Rahmu
1
Ich habe dies explizit versucht, bevor ich meine Antwort gepostet habe, und hatte keine Probleme damit. Ich habe den nautilus-Dateimanager verwendet, um eine Datei umzubenennen, die \ n enthält, um dies zu versuchen.
Peter
Du hast recht, so funktioniert es nicht. Ich weiß nicht, was ich getan habe, als ich das zuerst getestet habe. Versuchte es erneut und aktualisierte meine Antwort.
Peter
Nein, der Befehl ist in Ordnung, aber es gibt bereits eine ähnliche Lösung und versteckte Dateien werden nicht gezählt.
Xhienne
0

Neben der find-basierte Antwort von Stéphane vorgeschlagen , hier ist eine POSIX-konforme Antwort basiert auf ls:

ls -qf | tail -n +3 | wc -l
xhienne
quelle