Ich versuche herauszufinden, wie ich die Anzahl der Dateien in einem bestimmten Verzeichnis am besten ermitteln kann, wenn eine sehr große Anzahl von Dateien vorhanden ist (> 100.000).
Wenn so viele Dateien vorhanden sind, ls | wc -l
dauert die Ausführung ziemlich lange. Ich glaube, das liegt daran, dass die Namen aller Dateien zurückgegeben werden. Ich versuche, so wenig wie möglich von der Festplatten-E / A zu belegen.
Ich habe vergeblich mit einigen Shell- und Perl-Skripten experimentiert. Irgendwelche Ideen?
Antworten:
Standardmäßig werden
ls
die Namen sortiert. Dies kann eine Weile dauern, wenn viele vorhanden sind. Außerdem erfolgt keine Ausgabe, bis alle Namen gelesen und sortiert sind. Verwenden Sie diels -f
Option, um die Sortierung zu deaktivieren.Beachten Sie, dass dies auch ermöglichen
-a
, so.
,..
und andere Dateien mit Start.
gezählt.quelle
ls
.stat()
Aufrufls
für jede Datei.find
funktioniert also nichtstat()
schneller.ls -f
auch nichtstat()
. Aber natürlich beidesls
undfind
aufrufen,stat()
wenn bestimmte Optionen wiels -l
oder verwendet werdenfind -mtime
.ls -fR | wc -l
Der schnellste Weg ist ein speziell entwickeltes Programm wie das folgende:
Bei meinen Tests ohne Rücksicht auf den Cache habe ich jedes dieser Elemente ungefähr 50 Mal für dasselbe Verzeichnis ausgeführt, um einen cachebasierten Datenversatz zu vermeiden, und ich habe ungefähr die folgenden Leistungszahlen (in Echtzeit) erhalten:
Das letzte
dircnt
ist das Programm, das aus der obigen Quelle zusammengestellt wurde.EDIT 2016-09-26
Aufgrund der großen Nachfrage habe ich dieses Programm so umgeschrieben, dass es rekursiv ist, sodass es in Unterverzeichnisse verschoben wird und weiterhin Dateien und Verzeichnisse separat zählt.
Da es klar ist, dass einige Leute wissen wollen, wie man das alles macht, habe ich viele Kommentare im Code, um zu verdeutlichen, was los ist. Ich habe dies geschrieben und unter 64-Bit-Linux getestet, aber es sollte auf jedem POSIX-kompatiblen System funktionieren, einschließlich Microsoft Windows. Fehlerberichte sind willkommen. Ich freue mich, dies zu aktualisieren, wenn Sie es unter AIX oder OS / 400 oder was auch immer nicht zum Laufen bringen können.
Wie Sie sehen, ist es viel komplizierter als das Original und dies muss auch so sein: Es muss mindestens eine Funktion vorhanden sein, um rekursiv aufgerufen zu werden, es sei denn, Sie möchten, dass der Code sehr komplex wird (z. B. Verwalten eines Unterverzeichnisstapels und Verarbeiten dieser in einer einzelnen Schleife). Da wir Dateitypen überprüfen müssen, kommen Unterschiede zwischen verschiedenen Betriebssystemen, Standardbibliotheken usw. ins Spiel. Deshalb habe ich ein Programm geschrieben, das versucht, auf jedem System verwendet werden zu können, auf dem es kompiliert wird.
Es gibt nur sehr wenige Fehlerprüfungen und die
count
Funktion selbst meldet keine Fehler. Die einzigen Aufrufe, die wirklich fehlschlagen können, sindopendir
undstat
(wenn Sie kein Glück haben und ein System haben, in demdirent
der Dateityp bereits enthalten ist). Ich bin nicht paranoid, wenn es darum geht, die Gesamtlänge der Subdir-Pfadnamen zu überprüfen, aber theoretisch sollte das System keinen Pfadnamen zulassen, der länger als istPATH_MAX
. Wenn es Bedenken gibt, kann ich das beheben, aber es ist nur mehr Code, der jemandem erklärt werden muss, der das Schreiben von C lernt. Dieses Programm soll ein Beispiel dafür sein, wie man rekursiv in Unterverzeichnisse eintaucht.EDIT 2017-01-17
Ich habe zwei von @FlyingCodeMonkey vorgeschlagene Änderungen vorgenommen:
lstat
anstelle vonstat
. Dies ändert das Verhalten des Programms, wenn Sie Verzeichnisse in dem Verzeichnis, das Sie scannen, mit Symlinks verknüpft haben. Das vorherige Verhalten war, dass die Anzahl der Dateien im (verknüpften) Unterverzeichnis zur Gesamtzahl hinzugefügt wurde. Das neue Verhalten ist, dass das verknüpfte Verzeichnis als einzelne Datei zählt und sein Inhalt nicht gezählt wird.EDIT 2017-06-29
Mit etwas Glück ist dies die letzte Bearbeitung dieser Antwort :)
Ich habe diesen Code in ein GitHub-Repository kopiert , um das Abrufen des Codes zu vereinfachen (anstelle des Kopierens / Einfügens können Sie einfach die Quelle herunterladen ). Außerdem kann jeder leichter eine Änderung vorschlagen, indem er einen Pull sendet -Anfrage von GitHub.
Die Quelle ist unter Apache License 2.0 verfügbar. Patches * willkommen!
quelle
gcc -o dircnt dircnt.c
und die Verwendung ist wie./dircnt some_dir
Hast du versucht zu finden? Beispielsweise:
quelle
find /usr/share | wc -l
(~ 137.000 Dateien) etwa 25% schneller alsls -R /usr/share | wc -l
(~ 160.000 Zeilen einschließlich Verzeichnisnamen, Verzeichnissummen und Leerzeilen) beim ersten Durchlauf und mindestens doppelt so schnell beim Vergleich nachfolgender (zwischengespeicherter) Durchläufe.find
schneller ist alsls
aufgrund Ihrer Verwendungls
. Wenn Sie aufhören zu sortierenls
undfind
eine ähnliche Leistung erzielen.find, ls und perl wurden gegen 40 000 Dateien getestet: gleiche Geschwindigkeit (obwohl ich nicht versucht habe, den Cache zu leeren):
und mit perl opendir / readdir gleichzeitig:
Hinweis: Ich habe / bin / ls -f verwendet, um sicherzustellen, dass die Alias-Option umgangen wird, die sich möglicherweise etwas verlangsamt, und -f, um die Reihenfolge der Dateien zu vermeiden. ls ohne -f ist zweimal langsamer als find / perl, außer wenn ls mit -f verwendet wird, scheint es dieselbe Zeit zu sein:
Ich hätte auch gerne ein Skript, um das Dateisystem direkt ohne alle unnötigen Informationen zu fragen.
Tests basierend auf der Antwort von Peter van der Heijden, Glenn Jackman und Mark4o.
Thomas
quelle
ls -l | wc -l
einen Ordner auf einer externen 2,5-Zoll-Festplatte mit 1 Million Dateien ausführe, dauert es ungefähr 3 Minuten, bis der Vorgang abgeschlossen ist. Das zweite Mal dauert es 12 Sekunden IIRC. Auch dies kann möglicherweise auch von Ihrem Dateisystem abhängen wurde verwendetBtrfs
.$ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"' 1315029 real 0m0.580s user 0m0.302s sys 0m0.275s
Sie können die Ausgabe basierend auf Ihren Anforderungen ändern, aber hier ist ein Bash-Einzeiler, den ich geschrieben habe, um die Anzahl der Dateien in einer Reihe von numerisch benannten Verzeichnissen rekursiv zu zählen und zu melden.
Dies sucht rekursiv nach allen Dateien (nicht Verzeichnissen) im angegebenen Verzeichnis und gibt die Ergebnisse in einem Hash-ähnlichen Format zurück. Durch einfache Änderungen am Befehl find können Sie festlegen, welche Art von Dateien Sie zählen möchten usw.
Ergebnisse in etwa so:
quelle
ls -1 ${dir}
wird ohne mehr Leerzeichen nicht richtig funktionieren. Es gibt auch keine Garantie dafür, dass der von zurückgegebene Name weitergegeben werdenls
kannfind
, dals
nicht druckbare Zeichen für den menschlichen Verzehr entgehen. (mkdir $'oddly\nnamed\ndirectory'
wenn Sie einen besonders interessanten Testfall wünschen). Siehe Warum Sie die Ausgabe von ls (1) nicht analysieren solltenÜberraschenderweise ist ein nackter Fund für mich sehr vergleichbar mit ls-f
gegen
Natürlich verschieben sich die Werte auf der dritten Dezimalstelle jedes Mal ein wenig, wenn Sie eine dieser Optionen ausführen, sodass sie im Grunde genommen identisch sind. Beachten Sie jedoch, dass
find
eine zusätzliche Einheit zurückgegeben wird, da das tatsächliche Verzeichnis selbst gezählt wird (und, wie bereits erwähnt,ls -f
zwei zusätzliche Einheiten zurückgegeben werden, da es auch zählt. Und ..).quelle
Der Vollständigkeit halber nur hinzufügen. Die richtige Antwort wurde natürlich bereits von einer anderen Person gepostet, aber Sie können mit dem Baumprogramm auch eine Anzahl von Dateien und Verzeichnissen abrufen.
Führen Sie den Befehl aus
tree | tail -n 1
, um die letzte Zeile abzurufen, in der etwa "763 Verzeichnisse, 9290 Dateien" steht. Dies zählt Dateien und Ordner rekursiv, ausgenommen versteckte Dateien, die mit dem Flag hinzugefügt werden können-a
. Als Referenz dauerte es auf meinem Computer 4,8 Sekunden, bis der Baum mein gesamtes Ausgangsverzeichnis gezählt hatte, nämlich 24777 Verzeichnisse und 238680 Dateien.find -type f | wc -l
hat 5,3 Sekunden gedauert, eine halbe Sekunde länger, also denke ich, dass Baum in Bezug auf die Geschwindigkeit ziemlich wettbewerbsfähig ist.Solange Sie keine Unterordner haben, ist Baum eine schnelle und einfache Möglichkeit, die Dateien zu zählen.
Außerdem können Sie nur zum Spaß
tree | grep '^├'
nur die Dateien / Ordner im aktuellen Verzeichnis anzeigen - dies ist im Grunde eine viel langsamere Version vonls
.quelle
Brew install tail
für OS X.tail
sollte bereits auf Ihrem Mac OS X-System installiert sein.Schnelle Anzahl der Linux-Dateien
Die schnellste Anzahl an Linux-Dateien, die ich kenne, ist
Es gibt keine Notwendigkeit zu berufen grep! Aber wie bereits erwähnt, sollten Sie eine neue Datenbank haben (täglich aktualisiert durch einen Cron-Job oder manuell durch
sudo updatedb
).Vom Menschen ausfindig machen
Zusätzlich sollten Sie wissen, dass es auch die Verzeichnisse als Dateien zählt!
Übrigens: Wenn Sie einen Überblick über Ihre Dateien und Verzeichnisse auf Ihrem Systemtyp wünschen
Es gibt die Anzahl der Verzeichnisse, Dateien usw. aus.
quelle
Schreiben Sie dies hier, da ich nicht genügend Reputationspunkte habe, um eine Antwort zu kommentieren , aber ich darf meine eigene Antwort hinterlassen , was keinen Sinn ergibt. Wie auch immer...
In Bezug auf die Antwort von Christopher Schultz schlage ich vor, stat in lstat zu ändern und möglicherweise eine Grenzüberprüfung hinzuzufügen, um einen Pufferüberlauf zu vermeiden:
Der Vorschlag zur Verwendung von lstat besteht darin, das Folgen von Symlinks zu vermeiden, die zu Zyklen führen können, wenn ein Verzeichnis einen Symlink zu einem übergeordneten Verzeichnis enthält.
quelle
lstat
ein guter Vorschlag war und Sie Karma dafür verdienen. Dieser Vorschlag wurde in meinen Code oben und jetzt auf GitHub aufgenommen.Sie können versuchen, ob die Verwendung von
opendir()
undreaddir()
inPerl
schneller ist. Ein Beispiel für diese Funktion finden Sie hierquelle
Diese Antwort hier ist schneller als fast alles andere auf dieser Seite für sehr große, sehr verschachtelte Verzeichnisse:
https://serverfault.com/a/691372/84703
locate -r '.' | grep -c "^$PWD"
quelle
locate -c -r '/path'
wie in abu_bua LösungIch bin hierher gekommen, als ich versucht habe, die Dateien in einem Datensatz von ~ 10K-Ordnern mit jeweils ~ 10K-Dateien zu zählen. Das Problem bei vielen Ansätzen ist, dass sie implizit 100 Millionen Dateien statisieren, was ewig dauert.
Ich habe mir erlaubt, den Ansatz von christopher-schultz so zu erweitern, dass er das Übergeben von Verzeichnissen über args unterstützt (sein rekursiver Ansatz verwendet auch stat).
Legen Sie Folgendes in die Datei
dircnt_args.c
:Nach a können
gcc -o dircnt_args dircnt_args.c
Sie es folgendermaßen aufrufen:Bei 100 Millionen Dateien in 10-KB-Ordnern ist der oben genannte Vorgang recht schnell abgeschlossen (~ 5 Minuten für die erste Ausführung, Nachverfolgung des Caches: ~ 23 Sekunden).
Der einzige andere Ansatz, der in weniger als einer Stunde abgeschlossen war, war ls mit etwa 1 Minute im Cache :
ls -f /your/dirs/* | wc -l
. Die Zählung ist jedoch um ein paar Zeilenumbrüche pro Verzeichnis verschoben ...Anders als erwartet kehrte keiner meiner Versuche
find
innerhalb einer Stunde zurück: - /quelle
Der schnellste Weg unter Linux (die Frage ist als Linux gekennzeichnet) ist die Verwendung eines direkten Systemaufrufs. Hier ist ein kleines Programm, das Dateien (nur keine Verzeichnisse) in einem Verzeichnis zählt. Sie können Millionen von Dateien zählen und es ist ungefähr 2,5-mal schneller als "ls -f" und ungefähr 1,3-1,5-mal schneller als Christopher Schultz 'Antwort.
PS: Es ist nicht rekursiv, aber Sie können es ändern, um dies zu erreichen.
quelle
opendir
/ machtreaddir
, aber ich vermute, dass es am Ende auf fast denselben Code hinausläuft. Das Aufrufen von Systemaufrufen auf diese Weise ist auch nicht portierbar, und da das Linux-ABI nicht stabil ist, kann nicht garantiert werden, dass ein auf einem System kompiliertes Programm auf einem anderen System ordnungsgemäß funktioniert (obwohl es ein guter Rat ist, auf einem * NIX-System IMO alles aus dem Quellcode zu kompilieren ). Wenn Geschwindigkeit der Schlüssel ist, ist dies eine gute Lösung, wenn sie tatsächlich die Geschwindigkeit verbessert - ich habe die Programme nicht separat bewertet.ls
verbringt mehr Zeit damit, die-f
Dateinamen zu sortieren. Wenn Sie die Sortierung deaktivieren, wird Folgendes gespart:oder Sie können verwenden
find
:quelle
Ich habe festgestellt, dass es am schnellsten ist, die Speicherverarbeitung nicht zu verwenden, wenn Sie über eine große Datenmenge verfügen, als die Befehle "weiterzuleiten". Also habe ich das Ergebnis in einer Datei gespeichert und danach analysiert
quelle
Sie sollten "getdents" anstelle von ls / find verwenden
Hier ist ein sehr guter Artikel, der den Ansatz von getdents beschreibt.
http://be-n.com/spw/you-can-list-a-million-files-in-a-directory-but-not-with-ls.html
Hier ist der Auszug:
ls und praktisch jede andere Methode zum Auflisten eines Verzeichnisses (einschließlich python os.listdir, find.) basiert auf libc readdir (). Readdir () liest jedoch immer nur 32 KB Verzeichniseinträge gleichzeitig. Wenn sich also viele Dateien im selben Verzeichnis befinden (dh 500 Millionen Verzeichniseinträge), dauert das Lesen aller Verzeichniseinträge wahnsinnig lange , besonders auf einer langsamen Festplatte. Bei Verzeichnissen mit einer großen Anzahl von Dateien müssen Sie tiefer gehen als bei Tools, die auf readdir () basieren. Sie müssen den syscall getdents () direkt verwenden und nicht die Hilfsmethoden von libc.
Den C-Code zum Auflisten der Dateien mit getdents () finden Sie hier :
Sie müssen zwei Änderungen vornehmen, um schnell alle Dateien in einem Verzeichnis aufzulisten.
Erhöhen Sie zunächst die Puffergröße von X auf etwa 5 Megabyte.
Ändern Sie dann die Hauptschleife, in der die Informationen zu jeder Datei im Verzeichnis ausgedruckt werden, um Einträge mit inode == 0 zu überspringen. Ich habe dies durch Hinzufügen getan
In meinem Fall habe ich mich auch wirklich nur um die Dateinamen im Verzeichnis gekümmert, also habe ich auch die Anweisung printf () neu geschrieben, um nur den Dateinamen zu drucken.
Kompilieren Sie es (es benötigt keine externen Bibliotheken, daher ist es super einfach)
Jetzt lauf einfach
quelle
readdir()
nicht langsam ist. Ich brauche eine solide Figur, bevor ich glaube, dass es sich lohnt, die Portabilität für diesen Leistungsgewinn wegzuwerfen.Ich bevorzuge den folgenden Befehl, um die Änderungen in der Anzahl der Dateien in einem Verzeichnis zu verfolgen.
Der Befehl lässt ein Fenster geöffnet, um die Anzahl der Dateien im Verzeichnis mit einer Aktualisierungsrate von 0,1 Sekunden zu verfolgen.
quelle
ls | wc -l
ein Ordner mit Tausenden oder Millionen von Dateien in 0,01 Sekunden fertig ist? Selbst Ihrels
Lösung ist im Vergleich zu anderen Lösungen äußerst ineffizient. Und das OP will nur die Zählung bekommen, nicht da sitzen und die sich ändernde Ausgabe betrachtenwatch
Nun, ich habe das Handbuch nach diesem Kommentar gelesen und festgestellt, dass 0,01 s (nicht 0,1 s) eine unrealistische Zahl ist, da die Aktualisierungsrate der meisten PC-Bildschirme nur 60 Hz beträgt und dies die Frage in keiner Weise beantwortet. Das OP fragte nach "Fast Linux File Count für eine große Anzahl von Dateien". Sie haben auch keine verfügbaren Antworten vor dem Posten gelesenDie ersten 10 Directores mit der höchsten Anzahl von Dateien.
quelle