it-swarm.com.de

Ermitteln Sie die Anzahl der Dateien in einem Verzeichnis

Gibt es in Linux eine Methode, um die Anzahl der Dateien in einem Verzeichnis (dh unmittelbar untergeordnete Dateien) in O(1) (unabhängig von der Anzahl der Dateien) zu berechnen, ohne das Verzeichnis zuerst auflisten zu müssen? Wenn nicht O (1), gibt es einen einigermaßen effizienten Weg?

Ich suche nach einer Alternative zu ls | wc -l.

24
yassin

readdir ist nicht so teuer, wie Sie vielleicht denken. Der Kniff ist, dass Sie nicht jede Datei stat'ing und (optional) die Ausgabe von ls sortieren.

/bin/ls -1U | wc -l

vermeidet Aliasnamen in der Shell, sortiert die Ausgabe nicht und listet 1 Datei pro Zeile auf (nicht unbedingt erforderlich, wenn die Ausgabe in das WC geleitet wird).

Die ursprüngliche Frage kann umformuliert werden, da "speichert die Datenstruktur eines Verzeichnisses die Anzahl der Einträge?", Auf die die Antwort "Nein" lautet. Es gibt keine effizientere Methode zum Zählen von Dateien als readdir (2)/getdents (2).

36
Phil

Man kann die Anzahl der Unterverzeichnisse eines bestimmten Verzeichnisses abrufen, ohne die gesamte Liste zu durchlaufen, indem stat (1 (stat) (1) oder stat (2)) das angegebene Verzeichnis (1) durchsucht und die Anzahl der Links zu diesem Verzeichnis beobachtet wird. Ein gegebenes Verzeichnis mit N untergeordneten Verzeichnissen verfügt über eine Linkanzahl von N + 2, einen Link für den Eintrag ".." jedes Unterverzeichnisses und zwei für das "." und ".." Einträge des angegebenen Verzeichnisses.

Man kann jedoch nicht die Anzahl aller Dateien (ob reguläre Dateien oder Unterverzeichnisse) abrufen, ohne die gesamte Liste durchlaufen zu haben - das ist richtig.

Der Befehl "/ bin/ls -1U" erhält jedoch nicht alle Einträge. Es erhält only die Verzeichniseinträge, die nicht mit dem Punkt (.) Beginnen. Beispielsweise wird die ".profile" -Datei in vielen $ HOME-Login-Verzeichnissen nicht gezählt.

Man kann entweder den Befehl "/ bin/ls -f" oder den Befehl "/ bin/ls -Ua" verwenden, um das Sortieren zu vermeiden und alle Einträge abzurufen.

Für Ihre Zwecke zählt der Befehl "/ bin/ls -f" oder der Befehl "/ bin/ls -Ua" möglicherweise auch das "." und ".." Einträge, die sich in jedem Verzeichnis befinden. Sie müssen 2 von der Zählung abziehen, um zu vermeiden, dass diese beiden Einträge gezählt werden, wie im Folgenden:

expr `/bin/ls -f | wc -l` - 2     # Those are back ticks, not single quotes.

Die Option --format = einspaltig (-1) ist beim Befehl "/ bin/ls -Ua" nicht erforderlich, wenn die "ls" -Ausgabe weitergeleitet wird, wie in diesem Fall in "wc". Der Befehl "ls" schreibt seine Ausgabe automatisch in eine einzige Spalte, wenn die Ausgabe kein Terminal ist.

11
pj_

Die -U-Option für ls befindet sich nicht in POSIX und hat in OS Xs ls eine andere Bedeutung als GNU ls. Dies bedeutet, dass -t und -l Erstellzeiten anstelle von Änderungszeiten verwendet. -f ist in POSIX als XSI-Erweiterung enthalten. Das Handbuch von GNU ls beschreibt -f als do not sort, enable -aU, disable -ls --color und -U als do not sort; list entries in directory order.

POSIX beschreibt -f folgendermaßen:

Erzwingen Sie, dass jedes Argument als Verzeichnis interpretiert wird, und listen Sie den Namen auf, der in jedem Steckplatz gefunden wird. Diese Option schaltet -l, -t, -s und -r aus und aktiviert -a; Die Reihenfolge ist die Reihenfolge, in der die Einträge im Verzeichnis angezeigt werden.

Befehle wie ls|wc -l liefern das falsche Ergebnis, wenn Dateinamen Zeilenumbrüche enthalten.

In zsh kannst du so etwas machen:

a=(*(DN));echo ${#a}

D (glob_dots) enthält Dateien, deren Name mit einem Punkt beginnt, und N (null_glob) führt dazu, dass der Befehl nicht zu einem Fehler in einem leeren Verzeichnis führt.

Oder dasselbe in bash:

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

Wenn IFS ASCII Ziffern enthält, fügen Sie ${#a[@]} in doppelte Anführungszeichen ein. Fügen Sie shopt -u failglob hinzu, um sicherzustellen, dass failglob nicht gesetzt ist.

Eine tragbare Option ist die Verwendung von find:

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

grep -c / kann durch wc -l ersetzt werden, wenn Dateinamen keine Zeilenumbrüche enthalten. ! -name . -Prune ist eine tragbare Alternative zu -mindepth 1 -maxdepth 1.

Oder hier ist eine andere Alternative, die normalerweise keine Dateien enthält, deren Name mit einem Punkt beginnt:

set -- *;[ -e "$1" ]&&echo "$#"

Der obige Befehl enthält jedoch Dateien, deren Name mit einem Zeitraum beginnt, in dem eine Option wie dotglob in bash oder glob_dots in zsh gesetzt ist. Wenn * mit keiner Datei übereinstimmt, führt der Befehl zu einem Fehler in zsh mit den Standardeinstellungen.

3
nisetama

Ich habe diesen Befehl verwendet .. funktioniert wie ein Zauber ... nur um die maxdepth zu ändern ... das sind Unterverzeichnisse

find * -maxdepth 0 -type d -exec sh -c "echo -n {} ' ' ; ls -lR {} | wc -l" \;
2
user2991011

Ich denke, Sie können mit find mehr Kontrolle darüber haben:

find <path> -maxdepth 1 -type f -printf "." | wc -c
  • find -maxdepth 1 geht nicht tiefer in die Hierarchie der Dateien ein. 
  • -type f ermöglicht das Filtern nur von Dateien. Ebenso können Sie -type d für Verzeichnisse verwenden.
  • -printf "." druckt für jede Übereinstimmung einen Punkt.
  • wc -c zählt die Zeichen und zählt somit die Punkte, die von der print... erstellt wurden. Das bedeutet, wie viele Dateien im angegebenen Pfad vorhanden sind.
2
fedorqui

Meines Wissens gibt es keine bessere Alternative. Diese Informationen könnten für diese Frage nicht relevant sein, und Sie wissen möglicherweise bereits, dass Verzeichnisse unter Linux (im Allgemeinen unter Unix) nur eine spezielle Datei sind, die die Liste der anderen Dateien enthält System, aber dies ist die allgemeine Idee). Es gibt keinen Aufruf, die Gesamtzahl der Einträge zu ermitteln, ohne die gesamte Liste durchlaufen zu müssen. Bitte korrigieren Sie mich, wenn ich falsch liege. 

1
taskinoor

Für die Anzahl aller Dateien in einem aktuellen Verzeichnis versuchen Sie Folgendes:

ls -lR * | wc -l
0
chry