Gibt es einen Bash-Befehl, der die Anzahl der Dateien zählt, die einem Muster entsprechen?
Zum Beispiel möchte ich die Anzahl aller Dateien in einem Verzeichnis ermitteln, die diesem Muster entsprechen: log*
Dieser einfache Einzeiler sollte in jeder Shell funktionieren, nicht nur in Bash:
ls -1q log* | wc -l
ls -1q gibt Ihnen eine Zeile pro Datei, auch wenn sie Leerzeichen oder Sonderzeichen wie Zeilenumbrüche enthalten.
Die Ausgabe wird an wc -l weitergeleitet, wodurch die Anzahl der Zeilen gezählt wird.
-l
, da diesstat(2)
für jede Datei erforderlich ist und zum Zwecke des Zählens nichts hinzufügt.ls
, da es einen untergeordneten Prozess erstellt.log*
wird durch die Shell nicht erweitertls
, so würde ein einfachesecho
reichen.logs
in dem betreffenden Verzeichnis ein Verzeichnis aufgerufen wird, wird auch der Inhalt dieses Protokollverzeichnisses gezählt. Dies ist wahrscheinlich nicht beabsichtigt.Sie können dies
\n
mit bash sicher tun (dh nicht von Dateien mit Leerzeichen oder in ihrem Namen abgehört werden ):Sie müssen aktivieren,
nullglob
damit Sie das Literal*.log
im$logfiles
Array nicht erhalten, wenn keine Dateien übereinstimmen. ( Beispiele zum sicheren Zurücksetzen eines 'set -x' finden Sie unter "Rückgängigmachen" eines 'set -x' .)quelle
shopt -u nullglob
übersprungen werden, wennnullglob
es nicht deaktiviert wurde, als Sie begonnen haben.*.log
durch just*
werden Verzeichnisse gezählt. Wenn die Dateien, die Sie aufzählen möchten, die traditionelle Namenskonvention habenname.extension
, verwenden Sie*.*
.Viele Antworten hier, aber einige berücksichtigen nicht
-l
)*.log
statt warlog*
logs
, das übereinstimmtlog*
)Hier ist eine Lösung, die alle von ihnen behandelt:
Erläuterung:
-U
bewirktls
, dass die Einträge nicht sortiert werden, was bedeutet, dass nicht die gesamte Verzeichnisliste in den Speicher geladen werden muss-b
druckt Escapezeichen im C-Stil für nicht grafische Zeichen, was entscheidend dazu führt, dass Zeilenumbrüche als gedruckt werden\n
.-a
druckt alle Dateien aus, auch versteckte Dateien (nicht unbedingt erforderlich, wenn der Globuslog*
keine versteckten Dateien impliziert)-d
druckt Verzeichnisse aus, ohne zu versuchen, den Inhalt des Verzeichnisses aufzulisten , wasls
normalerweise der Fall ist-1
stellt sicher, dass es sich in einer Spalte befindet (ls tut dies automatisch, wenn in eine Pipe geschrieben wird, daher ist dies nicht unbedingt erforderlich)2>/dev/null
leitet stderr so um, dass bei 0 Protokolldateien die Fehlermeldung ignoriert wird. (Beachten Sie, dassshopt -s nullglob
dies dazu führen würde, dassls
stattdessen das gesamte Arbeitsverzeichnis aufgelistet wird.)wc -l
verbraucht die Verzeichnisliste, während sie generiert wird, sodass sich die Ausgabe vonls
zu keinem Zeitpunkt im Speicher befindet.--
Dateinamen werden vom Befehl getrennt--
, um nicht als Argumente für verstanden zu werdenls
(fallslog*
entfernt)Die Schale wird erweitern ,
log*
um die vollständige Liste der Dateien, die Speicher erschöpfen kann , wenn es sich um eine Menge von Dateien ist, so dann durch grep läuft besser sein:Letzteres verarbeitet extrem große Verzeichnisse von Dateien, ohne viel Speicher zu verbrauchen (obwohl es eine Subshell verwendet). Dies
-d
ist nicht mehr erforderlich, da nur der Inhalt des aktuellen Verzeichnisses aufgelistet wird.quelle
Für eine rekursive Suche:
wc -c
zählt die Anzahl der Zeichen in der Ausgabe vonfind
, während-printf x
angewiesen wirdfind
,x
für jedes Ergebnis ein einzelnes zu drucken .Führen Sie für eine nicht rekursive Suche Folgendes aus:
quelle
-name '*.log'
werden alle Dateien gezählt, was ich für meinen Anwendungsfall benötigt habe. Auch das Flag -maxdepth ist äußerst nützlich, danke!find
; Drucken Sie einfach etwas anderes als den wörtlichen Dateinamen.Die akzeptierte Antwort auf diese Frage ist falsch, aber ich habe eine geringe Wiederholungszahl und kann daher keinen Kommentar hinzufügen.
Die richtige Antwort auf diese Frage gibt Mat:
Das Problem mit der akzeptierten Antwort ist, dass wc -l die Anzahl der Zeilenumbruchzeichen zählt und diese auch dann zählt, wenn sie als '?' Auf dem Terminal gedruckt werden. in der Ausgabe von 'ls -l'. Dies bedeutet, dass die akzeptierte Antwort fehlschlägt, wenn ein Dateiname ein Zeilenumbruchzeichen enthält. Ich habe den vorgeschlagenen Befehl getestet:
und es wird fälschlicherweise der Wert 2 gemeldet, selbst wenn nur 1 Datei mit dem Muster übereinstimmt, dessen Name zufällig ein Zeilenumbruchzeichen enthält. Beispielsweise:
quelle
Wenn Sie viele Dateien haben und die elegante
shopt -s nullglob
und Bash-Array-Lösung nicht verwenden möchten , können Sie find usw. verwenden, solange Sie den Dateinamen nicht ausdrucken (der möglicherweise Zeilenumbrüche enthält).Dadurch werden alle Dateien gefunden, die mit log * übereinstimmen und nicht mit
.*
- beginnen. "Not name. *" Ist redundant, es ist jedoch wichtig zu beachten, dass die Standardeinstellung für "ls" darin besteht, keine Punktdateien anzuzeigen, sondern die Standardeinstellung denn zu finden ist, sie einzuschließen.Dies ist eine korrekte Antwort und behandelt alle Arten von Dateinamen, die Sie darauf werfen können, da der Dateiname niemals zwischen Befehlen weitergegeben wird.
Aber die
shopt nullglob
Antwort ist die beste Antwort!quelle
find
vs vsls
sind zwei verschiedene Möglichkeiten, das Problem zu lösen.find
ist nicht immer auf einer Maschine vorhanden, ist aberls
normalerweise,find
wahrscheinlich nicht gibt, hat auch nicht all diese ausgefallenen Optionenls
.-maxdepth 1
find
macht dies standardmäßig. Dies kann zu Verwirrung führen, wenn man nicht merkt, dass es einen versteckten untergeordneten Ordner gibt, und es kann unterls
bestimmten Umständen vorteilhaft sein, ihn zu verwenden , der standardmäßig keine versteckten Dateien meldet.Hier ist mein Einzeiler dafür.
quelle
set --
wird also nichts anderes getan, als uns darauf$#
Mit der Option -R können Sie die Dateien zusammen mit denen in den rekursiven Verzeichnissen suchen
Sie können Muster auf dem Grep verwenden
quelle
Ich habe über diese Antwort viel nachgedacht, besonders angesichts der Dinge , die man nicht analysiert . Zuerst habe ich es versucht
was funktionierte, wenn es nur einen Dateinamen wie gab
aber fehlgeschlagen, wenn ich einen Dateinamen wie diesen gemacht habe
Ich habe mir endlich ausgedacht, was ich unten schreibe. Hinweis Ich habe versucht, alle Dateien im Verzeichnis zu ermitteln (ohne Unterverzeichnisse). Ich denke, zusammen mit den Antworten von @Mat und @Dan_Yard sowie den meisten Anforderungen von @mogsie (ich bin mir nicht sicher, was das Gedächtnis betrifft.) Ich denke, die Antwort von @mogsie ist richtig. Aber ich versuche immer, mich vom Parsen fernzuhalten,
ls
es sei denn, es handelt sich um eine äußerst spezifische Situation.Lesbarer:
Hierbei wird eine Suche speziell für Dateien durchgeführt, wobei die Ausgabe durch ein Nullzeichen begrenzt wird (um Probleme mit Leerzeichen und Zeilenvorschüben zu vermeiden) und anschließend die Anzahl der Nullzeichen gezählt wird. Die Anzahl der Dateien ist eins weniger als die Anzahl der Nullzeichen, da am Ende ein Nullzeichen steht.
Um die Frage des OP zu beantworten, sind zwei Fälle zu berücksichtigen
1) Nicht rekursive Suche:
2) Rekursive Suche. Beachten Sie, dass der Inhalt des
-name
Parameters möglicherweise geändert werden muss, um ein etwas anderes Verhalten zu erzielen (versteckte Dateien usw.).Wenn jemand kommentieren möchte, wie diese Antworten mit denen verglichen werden, die ich in dieser Antwort erwähnt habe, tun Sie dies bitte.
Beachten Sie, dass ich zu diesem Gedankenprozess gekommen bin, als ich diese Antwort erhalten habe .
quelle
Ein wichtiger Kommentar
(nicht genug Ruf, um zu kommentieren)
Das ist BUGGY :
Wenn
shopt -s nullglob
dies festgelegt ist, wird die Anzahl ALLER regulären Dateien gedruckt , nicht nur derjenigen mit dem Muster (getestet unter CentOS-8 und Cygwin). Wer weiß, welche anderen bedeutungslosen Fehler esls
gibt?Das ist RICHTIG und viel schneller:
Es macht den erwarteten Job.
Und die Laufzeiten sind unterschiedlich.
Das erste:
0.006
unter CentOS und0.083
unter Cygwin (falls es mit Vorsicht verwendet wird).Der 2.:
0.000
auf CentOS und0.003
auf Cygwin.quelle
Folgendes mache ich immer:
quelle
awk 'END{print NR}'
sollte gleichbedeutend sein mitwc -l
.Sie können einen solchen Befehl einfach mithilfe einer Shell-Funktion definieren. Diese Methode erfordert kein externes Programm und erzeugt keinen untergeordneten Prozess. Es wird kein gefährliches
ls
Parsen versucht und „Sonderzeichen“ (Leerzeichen, Zeilenumbrüche, Backslashes usw.) werden problemlos verarbeitet. Es basiert nur auf dem von der Shell bereitgestellten Mechanismus zur Erweiterung des Dateinamens. Es ist kompatibel mit mindestens sh, bash und zsh.Die folgende Zeile definiert eine aufgerufene Funktion,
count
die die Anzahl der Argumente ausgibt, mit denen sie aufgerufen wurde.Nennen Sie es einfach mit dem gewünschten Muster:
Damit das Ergebnis korrekt ist, wenn das Globbing-Muster nicht übereinstimmt, muss die Shell-Option
nullglob
(oderfailglob
- das ist das Standardverhalten bei zsh) zum Zeitpunkt der Erweiterung festgelegt werden. Es kann wie folgt eingestellt werden:Je nachdem, was Sie zählen möchten, könnte Sie auch die Shell-Option interessieren
dotglob
.Leider ist es zumindest mit bash nicht einfach, diese Optionen lokal festzulegen. Wenn Sie sie nicht global festlegen möchten, besteht die einfachste Lösung darin, die Funktion auf diese kompliziertere Weise zu verwenden:
Wenn Sie die leichtgewichtige Syntax wiederherstellen möchten
count log*
oder wenn Sie wirklich vermeiden möchten, dass eine Unterschale erzeugt wird, können Sie Folgendes hacken:Als Bonus ist diese Funktion allgemeiner. Zum Beispiel:
Durch Verwandeln der Funktion in eine Skriptdatei (oder ein gleichwertiges C-Programm), die über PATH aufgerufen werden kann, kann sie auch mit Programmen wie
find
und zusammengesetzt werdenxargs
:quelle
Dies bedeutet, dass eine Datei pro Zeile aufgelistet und dann an den Wortzählbefehl weitergeleitet wird, wobei der Parameter auf die Anzahl der Zeilen umgeschaltet wird.
quelle
Um alles zu zählen, leiten Sie ls einfach zur Wortzählzeile:
Um mit dem Muster zu zählen, leiten Sie zuerst an grep weiter:
quelle