CentOS 5.9
Ich bin neulich auf ein Problem gestoßen, bei dem ein Verzeichnis viele Dateien enthielt. Um es zu zählen, rannte ichls -l /foo/foo2/ | wc -l
Es stellte sich heraus, dass sich über 1 Million Dateien in einem einzigen Verzeichnis befanden (lange Geschichte - die Ursache wird behoben).
Meine Frage ist: Gibt es einen schnelleren Weg, um zu zählen? Was wäre der effizienteste Weg, um die Zählung zu erhalten?
ls -l|wc -l
wäre wegen der Gesamtanzahl der Blöcke in der erstenls -l
Ausgabezeile um eins gesperrt-A
Flags vermieden werden .-l
ist auch problematisch, weil die Datei Metadaten liest, um das erweiterte Listenformat zu generieren. Das Erzwingen von NOT-l
durch Verwendung\ls
ist eine viel bessere Option (-1
wird bei der Weiterleitung der Ausgabe vorausgesetzt). Die beste Lösung finden Sie hier in der Antwort von Gilles .ls -l
gibt weder versteckte Dateien noch die Einträge.
und aus..
.ls -a
Die Ausgabe enthält versteckte Dateien, einschließlich.
und,..
während diels -A
Ausgabe versteckte Dateien ohne.
und enthält..
. In Gilles 'Antwort bewirkt die Bash-dotglob
Shell-Option, dass die Erweiterung versteckte Dateien ohne.
und enthält..
.Antworten:
Kurze Antwort:
(Dies beinhaltet
.
und..
, also subtrahiere 2)Wenn Sie die Dateien in einem Verzeichnis auflisten, können drei häufige Probleme auftreten:
ls
Befehl machen das.stat
zum Abrufen von Metadaten zu jedem Verzeichniseintrag, z. B. ob es sich um ein Verzeichnis handelt.# 3 ist bei weitem am teuersten, da für jede Datei ein Inode geladen werden muss. Im Vergleich dazu sind alle für # 1 benötigten Dateinamen in wenigen Blöcken kompakt gespeichert. # 2 verschwendet etwas CPU-Zeit, ist aber oft kein Deal Breaker.
Wenn die Dateinamen keine Zeilenumbrüche enthalten, werden Sie durch eine einfache Angabe darüber informiert,
ls -A | wc -l
wie viele Dateien sich im Verzeichnis befinden. Beachten Sie, dass, wenn Sie einen Alias für habenls
, dies möglicherweise einen Aufruf von auslösen kannstat
(z. B.ls --color
oderls -F
den Dateityp kennen müssen, für den ein Aufruf erforderlich iststat
), also von der Befehlszeile aus,command ls -A | wc -l
oder\ls -A | wc -l
um einen Alias zu vermeiden.Wenn der Dateiname Zeilenumbrüche enthält, hängt es von der Unix-Variante ab, ob Zeilenumbrüche aufgelistet werden oder nicht. GNU coreutils und BusyBox werden standardmäßig
?
für eine neue Zeile angezeigt, damit sie sicher sind.Rufen Sie
ls -f
an, um die Einträge aufzulisten, ohne sie zu sortieren (Nr. 2). Dies schaltet sich automatisch ein-a
(zumindest bei modernen Systemen). Die-f
Option ist in POSIX, aber mit optionalem Status. Die meisten Implementierungen unterstützen dies, BusyBox jedoch nicht. Die Option-q
ersetzt nicht druckbare Zeichen einschließlich Zeilenumbrüchen durch?
; Es ist POSIX, wird jedoch von BusyBox nicht unterstützt. Lassen Sie es daher aus, wenn Sie BusyBox-Unterstützung benötigen, und zahlen Sie dabei zu viele Dateien, deren Name ein Zeilenumbruchzeichen enthält.Wenn das Verzeichnis keine Unterverzeichnisse enthält,
find
werdenstat
die Einträge in den meisten Versionen von nicht abgerufen (Blattverzeichnisoptimierung: Ein Verzeichnis mit einer Linkanzahl von 2 kann keine Unterverzeichnisse enthalten. Daherfind
müssen die Metadaten der Einträge nur nachgeschlagen werden, wenn ein Bedingung wie es-type
erforderlich ist). Diesfind . | wc -l
ist eine tragbare und schnelle Methode zum Zählen von Dateien in einem Verzeichnis, vorausgesetzt, das Verzeichnis enthält keine Unterverzeichnisse und kein Dateiname enthält eine neue Zeile.Wenn das Verzeichnis keine Unterverzeichnisse enthält, die Dateinamen jedoch möglicherweise Zeilenumbrüche enthalten, probieren Sie eines dieser Verzeichnisse aus (das zweite sollte schneller sein, wenn es unterstützt wird, dies ist jedoch möglicherweise nicht erkennbar).
Auf der anderen Seite, verwenden Sie nicht ,
find
wenn das Verzeichnis Verzeichnisse hat: auchfind . -maxdepth 1
Anrufestat
auf jedem Eintrag (zumindest mit GNU finden und BusyBox zu finden). Sie vermeiden das Sortieren (Nr. 2), zahlen jedoch den Preis für eine Inode-Suche (Nr. 3), die die Leistung beeinträchtigt.In der Shell ohne externe Tools können Sie die Dateien im aktuellen Verzeichnis mit zählen
set -- *; echo $#
. Dadurch werden Punktdateien (Dateien, deren Name mit ".
1" beginnt ) übersehen und in einem leeren Verzeichnis wird "1" anstelle von "0" angezeigt. Dies ist der schnellste Weg, um Dateien in kleinen Verzeichnissen zu zählen, da kein externes Programm gestartet werden muss, aber (mit Ausnahme von zsh) Zeit für größere Verzeichnisse aufgrund des Sortierschritts (# 2) verschwendet wird.In Bash ist dies eine zuverlässige Methode, um die Dateien im aktuellen Verzeichnis zu zählen:
In ksh93 ist dies eine zuverlässige Methode, um die Dateien im aktuellen Verzeichnis zu zählen:
In zsh ist dies eine zuverlässige Methode, um die Dateien im aktuellen Verzeichnis zu zählen:
Wenn Sie die haben
mark_dirs
Option gesetzt, stellen Sie sicher, um sie auszuschalten:a=(*(DNoN^M))
.In jeder POSIX-Shell ist dies eine zuverlässige Methode, um die Dateien im aktuellen Verzeichnis zu zählen:
Alle diese Methoden sortieren die Dateinamen mit Ausnahme der zsh-Methode.
quelle
find -maxdepth 1
problemlos möglich ist,\ls -U
solange Sie nichts wie eine-type
Erklärung hinzufügen , die weitere Überprüfungen erfordert. Sind Sie sicher, dass GNU tatsächlich Aufrufe findetstat
? Sogar die Verlangsamungfind -type
ist nichts im Vergleich dazu, wie vielels -l
Fehler Sie machen, wenn Sie Dateidetails zurückgeben. Auf der anderen Seite verwendet der Gewinner der klaren Geschwindigkeitzsh
das nicht sortierende Glob. (Sortierte Globs sind 2x langsamer alsls
nicht sortierte, 2x schneller). Ich frage mich, ob Dateisystemtypen diese Ergebnisse erheblich beeinflussen würden.strace
. Dies gilt nur, wenn das Verzeichnis Unterverzeichnisse hat: Andernfalls setztfind
die Blattverzeichnisoptimierung ein (auch ohne-maxdepth 1
), das hätte ich erwähnen sollen. Viele Dinge können das Ergebnis beeinflussen, einschließlich des Dateisystemtyps (Aufrufenstat
ist bei Dateisystemen, die Verzeichnisse als lineare Listen darstellen, viel teurer als bei Dateisystemen, die Verzeichnisse als Bäume darstellen), ob die Inodes alle zusammen erstellt wurden und daher in der Nähe sind auf der Festplatte, kalter oder heißer Cache usw.ls -f
dies der zuverlässige Weg, um Anrufe zu verhindernstat
- dies wird heute oft einfach als "Ausgabe ist nicht sortiert" (was auch dazu führt) beschrieben und beinhaltet.
und..
.-A
und-U
sind keine Standardoptionen.\ls -afq *[0-9].pdb | wc -l
version sh (AT&T Research) 93u+ 2012-08-01
auf meinem Debian-basierten System,FIGNORE
scheint nicht zu funktionieren. Die.
und..
Einträge werden in das resultierende Array aufgenommenIst auf meinem Rechner erheblich schneller, aber das lokale
.
Verzeichnis wird zur Zählung hinzugefügt.quelle
-type
Parameter nicht verwenden ,find
sollte es schneller sein alsls
-mindepth 1
, um das Verzeichnis selbst auszulassen.ls -1U
Bevor die Pipe etwas weniger Ressourcen verbraucht, da sie nicht versucht, die Dateieinträge zu sortieren, liest sie sie nur so, wie sie im Ordner auf der Festplatte sortiert sind. Es produziert auch weniger Output, was etwas weniger Arbeit bedeutetwc
.Sie könnten auch verwenden,
ls -f
was mehr oder weniger eine Abkürzung für istls -1aU
.Ich weiß nicht, ob es eine ressourceneffiziente Möglichkeit gibt, dies über einen Befehl ohne Piping zu tun.
quelle
Ein weiterer Vergleichspunkt. Dieses C-Programm ist zwar kein Shell-Oneliner, macht aber nichts überflüssiges. Beachten Sie, dass versteckte Dateien ignoriert werden, um mit der Ausgabe von übereinzustimmen
ls|wc -l
(ls -l|wc -l
ist aufgrund der Gesamtanzahl der Blöcke in der ersten Ausgabezeile um eins deaktiviert).quelle
readdir()
stdio-API erhöht den Aufwand und gibt Ihnen keine Kontrolle über die Größe des Puffers, der an den zugrunde liegenden Systemaufruf (getdents
unter Linux)Du könntest es versuchen
perl -e 'opendir($dh,".");$i=0;while(readdir $dh){$i++};print "$i\n";'
Es wäre interessant, das Timing mit Ihrer Pfeife zu vergleichen.
quelle
find -maxdepth 1 | wc -l
,\ls -AU | wc -l
und diezsh
Basis nicht Sortieranlage glob und Array count). Mit anderen Worten, es übertrifft die Optionen mit verschiedenen Ineffizienzen wie dem Sortieren oder Lesen von fremden Dateieigenschaften. Ich würde sagen, da es Ihnen auch nichts einbringt, lohnt es sich nicht, eine einfachere Lösung zu verwenden, es sei denn, Sie befinden sich bereits in Perl :).
und..
in die Anzahl einschließt. Sie müssen also zwei davon abziehen, um die tatsächliche Anzahl der Dateien (und Unterverzeichnisse) zu erhalten. In modernen Perlperl -E 'opendir $dh, "."; $i++ while readdir $dh; say $i - 2'
würde es tun.Aus dieser Antwort kann ich mir diese als mögliche Lösung vorstellen.
Kopieren Sie das obige C-Programm in das Verzeichnis, in dem die Dateien aufgelistet werden müssen. Führen Sie dann diese Befehle aus:
quelle
ls -f
Filtern Sie überhaupt nicht nach , sondernd_type
nur nachd->d_ino != 0
. 3) subtrahiere 2 für.
und..
.ls -f
.Eine reine Bash-Lösung, die kein externes Programm erfordert, aber nicht weiß, wie effizient sie ist:
quelle
Am ressourcenschonendsten wären wahrscheinlich keine externen Prozessaufrufe. Also würde ich wetten auf ...
quelle
Nachdem Sie das Problem in der Antwort von @Joel behoben und
.
als Datei hinzugefügt haben :find /foo/foo2 -maxdepth 1 | tail -n +2 | wc -l
tail
Entfernt einfach die erste Zeile, was bedeutet, dass diese.
nicht mehr gezählt wird.quelle
wc
Eingabezeile ist nicht sehr effizient, da der Overhead in Bezug auf die Eingabegröße linear zunimmt . In diesem Fall können Sie die Endzählung einfach verringern, um zu kompensieren, dass sie um eins abweicht. Dies ist eine zeitlich konstante Operation:echo $(( $(find /foo/foo2 -maxdepth 1 | wc -l) - 1))
let count = $(find /foo/foo2 -maxdepth 1 | wc -l) - 2
os.listdir () in Python kann die Arbeit für Sie erledigen. Es gibt ein Array des Inhalts des Verzeichnisses ohne das spezielle '.' und '..' Dateien. Außerdem brauchen Sie sich keine Sorgen um Dateien mit Sonderzeichen wie '\ n' im Namen zu machen.
Es folgt die Zeit, die der obige Python-Befehl im Vergleich zum Befehl 'ls -Af' benötigt.
quelle
ls -1 | wc -l
fällt mir sofort ein. Obls -1U
das schneller ist alsls -1
rein akademisch - der Unterschied sollte aber bei sehr großen Verzeichnissen vernachlässigbar sein.quelle
Um Unterverzeichnisse von der Zählung auszuschließen , gibt es eine Variation der von Gilles akzeptierten Antwort:
Die äußere
$(( ))
arithmetische Erweiterung subtrahiert die Ausgabe der zweiten$( )
Unterschale von der ersten$( )
. Das erste$( )
ist genau Gilles von oben. Die zweite$( )
gibt die Anzahl der Verzeichnisse aus, die mit dem Ziel "verknüpft" sind. Dies kommt vonls -od
(aufls -ld
Wunsch als Ersatz ), wobei die Spalte, in der die Anzahl der festen Links aufgeführt ist, diese als besondere Bedeutung für Verzeichnisse hat. Der „Link“ count enthält.
,..
und alle Unterverzeichnisse.Ich habe die Leistung nicht getestet, aber es scheint ähnlich zu sein. Es fügt eine Statistik des Zielverzeichnisses und einen zusätzlichen Aufwand für die hinzugefügte Subshell und Pipe hinzu.
quelle
Ich würde denken, dass echo * effizienter ist als jeder 'ls'-Befehl:
quelle
echo 'Hello World'|wc -w
produziert2
.