Gibt es etwas schnelleres als finden. | wc -l` um Dateien in einem Verzeichnis zu zählen?

8

Nicht selten muss ich die Anzahl der Dateien in einem Verzeichnis zählen, manchmal sind es Millionen.

Gibt es einen besseren Weg, als sie nur aufzuzählen und zu zählen find . | wc -l? Gibt es eine Art Dateisystemaufruf, den Sie unter ext3 / 4 ausführen können und der weniger E / A-intensiv ist?

MattPark
quelle
3
Sie zählen nicht nur Dateien, sondern auch Verzeichnisse. Wenn Sie nur Dateien zählen möchten, verwenden Sie "find. -Type f | wc -l". Wenn Sie symbolische Links und reguläre Dateien zählen möchten, verwenden Sie "find. -Type f -oder -type l | wc -l"
FSMaxB
Ein Verzeichnis ist eine Art Datei, ebenso wie Geräte, Symlinks und Sockets. Normale Dateien sind eine Teilmenge von Dateien.
Toby Speight
1
Das Beispiel, das Sie geben, legt nahe, dass Sie eine rekursive Zählung wünschen - wenn nicht, dann brauchen Sie find -maxdepth 1. Beachten Sie, dass Sie mit Ihrem aktuellen Ansatz jeden Namen, der ein Zeilenumbruchzeichen enthält, doppelt zählen.
Toby Speight

Antworten:

13

Keine grundlegende Beschleunigung, aber zumindest etwas :)

find . -printf \\n | wc -l

Sie müssen die Liste der Dateinamen wirklich nicht übergeben, nur die Zeilenumbrüche reichen aus. Diese Variante ist auf meinem Ubuntu 12.04.3 etwa 15% schneller, wenn die Verzeichnisse im RAM zwischengespeichert werden. Außerdem funktioniert diese Variante korrekt mit Dateinamen, die Zeilenumbrüche enthalten.

Interessanterweise scheint diese Variante etwas langsamer zu sein als die obige:

find . -printf x | wc -c

Sonderfall - aber sehr schnell

Wenn sich das Verzeichnis in einem eigenen Dateisystem befindet, können Sie einfach die Inodes zählen:

df -i .

Wenn sich die Anzahl der Verzeichnisse und Dateien in anderen als den gezählten Verzeichnissen nicht wesentlich ändert, können Sie diese bekannte Anzahl einfach vom aktuellen df -iErgebnis abziehen . Auf diese Weise können Sie die Dateien und Verzeichnisse sehr schnell zählen.

Pabouk
quelle
"Diese Variante ist ungefähr 15% schneller ..." Ich frage mich, ob es einen praktischen Trick gibt, mit dem Sie diese zeitlich festlegen können.
Brian Z
4
@BrianZ: Sie können einen Befehl zeitlich festlegen, indem Sie dem Befehl die Zeit voranstellen. time find /usr/src/ -printf \\n | wc -lkönnen Sie die Caches zwischen den Läufen mitsudo sync && sudo sysctl -w vm.drop_caches=3
MattPark
Daher konnte ich bei beiden Optionen ohne Caching eine konstante Geschwindigkeitssteigerung von 2% feststellen. Also ja, das ist eine ziemlich coole Art, es zu tun. Das Zählen der Inodes ist definitiv das Beste, wenn Ihre Umgebung dafür eingerichtet ist. Ich hatte nicht darüber nachgedacht.
MattPark
Soll -printf xdas gleiche sein wie -printf '\0'? Ich sehe es nicht in den Dokumenten erwähnt.
CMCDragonkai
@CMCDragonkai: Die Aktion -printffunktioniert ähnlich wie die printf()Funktion in C, mit dem Hauptunterschied, dass die %Anweisungen eine andere Bedeutung haben. Die Aktion wird für jede gefundene Datei aufgerufen. Dies bedeutet, dass -printf xdas Zeichen xfür jede gefundene Datei gedruckt wird (probieren Sie es aus!) Und -printf '\0'das Zeichen NULL (ASCII-Code 0) für jede gefundene Datei gedruckt wird. -printf '\0'hat keine besondere Bedeutung. Beide funktionieren im Beispiel mit wc -cin dieser Antwort gleich.
Pabouk
3

Ich habe ffcnt genau zu diesem Zweck geschrieben. Es ruft den physischen Versatz der Verzeichnisse selbst mit dem fiemapioctl ab und plant dann die Verzeichnisüberquerung in mehreren aufeinander folgenden Durchläufen, um den Direktzugriff zu reduzieren. Ob Sie im Vergleich zu tatsächlich eine Beschleunigung erzielen, find | wc hängt von mehreren Faktoren ab:

  • Dateisystemtyp: Dateisysteme wie ext4, die fiemapioctl unterstützen, profitieren am meisten
  • Direktzugriffsgeschwindigkeit: Festplatten profitieren weit mehr als SSDs
  • Verzeichnislayout: Je höher die Anzahl der verschachtelten Verzeichnisse, desto größer das Optimierungspotenzial

Das (erneute) Mounten mit relatimeoder nodiratimekann sogar die Geschwindigkeit (für alle Methoden) verbessern, wenn die Zugriffe andernfalls zu Metadatenaktualisierungen führen würden.

the8472
quelle
Dieser letzte Satz ist ein lohnender Tipp! Ich denke, der Link zu Ihrem Programm würde verbessert, wenn Sie eine Zusammenfassung der Funktionsweise hinzufügen würden. Wir bevorzugen Antworten, die an sich vollständig sind, falls der verknüpften Ressource etwas Schlimmes passiert (aber natürlich auch den Link behalten).
Toby Speight
2

Eigentlich auf meinem System (Arch Linux) dieser Befehl

   ls -A | wc -l

ist schneller als alle oben genannten:

   $ time find . | wc -l
  1893

   real    0m0.027s
   user    0m0.004s
   sys     0m0.004s
   $ time find . -printf \\n  | wc -l
   1893

   real    0m0.009s
   user    0m0.000s
   sys     0m0.008s
   $ time find . -printf x  | wc -c
   1893

   real    0m0.009s
   user    0m0.000s
   sys     0m0.008s
   $ time ls -A | wc -l
   1892

   real    0m0.007s
   user    0m0.000s
   sys     0m0.004s
MariusMatutiae
quelle
Ich denke, das Problem mit ls ist, dass es oft etwas zurückgibt, /bin/ls: Argument list too longwenn Sie Globbing verwenden, aber andererseits kann es auch rekursiv wie find funktionieren. Vielleicht ist das etwas zu beachten, verwenden Sie find nicht, wenn es nicht benötigt wird.
MattPark
Es scheint so spät (viele Jahre) zu sein, dies zu kommentieren, aber ls -Anur die Dateien im aktuellen Verzeichnis aufzulisten, während findohne -maxdepth 1Argument eine rekursive Suche in allen Unterverzeichnissen durchgeführt wird.
Luciano