Antworten:
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.
ls, da es einen untergeordneten Prozess erstellt. log*wird durch die Shell nicht erweitert ls, so würde ein einfaches echoreichen.
logsin dem betreffenden Verzeichnis ein Verzeichnis aufgerufen wird, wird auch der Inhalt dieses Protokollverzeichnisses gezählt. Dies ist wahrscheinlich nicht beabsichtigt.
Sie können dies \nmit bash sicher tun (dh nicht von Dateien mit Leerzeichen oder in ihrem Namen abgehört werden ):
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
Sie müssen aktivieren, nullglobdamit Sie das Literal *.logim $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' .)
shopt -u nullglobübersprungen werden, wenn nullglobes nicht deaktiviert wurde, als Sie begonnen haben.
*.logdurch just *werden Verzeichnisse gezählt. Wenn die Dateien, die Sie aufzählen möchten, die traditionelle Namenskonvention haben name.extension, verwenden Sie *.*.
Viele Antworten hier, aber einige berücksichtigen nicht
-l)*.logstatt warlog*logs, das übereinstimmt log*)Hier ist eine Lösung, die alle von ihnen behandelt:
ls 2>/dev/null -Ubad1 -- log* | wc -l
Erläuterung:
-Ubewirkt ls, dass die Einträge nicht sortiert werden, was bedeutet, dass nicht die gesamte Verzeichnisliste in den Speicher geladen werden muss-bdruckt Escapezeichen im C-Stil für nicht grafische Zeichen, was entscheidend dazu führt, dass Zeilenumbrüche als gedruckt werden \n.-adruckt alle Dateien aus, auch versteckte Dateien (nicht unbedingt erforderlich, wenn der Globus log*keine versteckten Dateien impliziert)-ddruckt Verzeichnisse aus, ohne zu versuchen, den Inhalt des Verzeichnisses aufzulisten , was lsnormalerweise 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/nullleitet stderr so um, dass bei 0 Protokolldateien die Fehlermeldung ignoriert wird. (Beachten Sie, dass shopt -s nullglobdies dazu führen würde, dass lsstattdessen das gesamte Arbeitsverzeichnis aufgelistet wird.)wc -lverbraucht die Verzeichnisliste, während sie generiert wird, sodass sich die Ausgabe von lszu keinem Zeitpunkt im Speicher befindet.--Dateinamen werden vom Befehl getrennt --, um nicht als Argumente für verstanden zu werden ls(falls log*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:
ls -Uba1 | grep ^log | wc -l
Letzteres verarbeitet extrem große Verzeichnisse von Dateien, ohne viel Speicher zu verbrauchen (obwohl es eine Subshell verwendet). Dies -dist nicht mehr erforderlich, da nur der Inhalt des aktuellen Verzeichnisses aufgelistet wird.
Für eine rekursive Suche:
find . -type f -name '*.log' -printf x | wc -c
wc -czählt die Anzahl der Zeichen in der Ausgabe von find, während -printf xangewiesen wird find, xfür jedes Ergebnis ein einzelnes zu drucken .
Führen Sie für eine nicht rekursive Suche Folgendes aus:
find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c
-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:
shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}
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:
ls -l log* | wc -l
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:
touch log$'\n'def
ls log* -l | wc -l
Wenn Sie viele Dateien haben und die elegante shopt -s nullglobund 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).
find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l
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 nullglobAntwort ist die beste Antwort!
findvs vs lssind zwei verschiedene Möglichkeiten, das Problem zu lösen. findist nicht immer auf einer Maschine vorhanden, ist aber lsnormalerweise,
findwahrscheinlich nicht gibt, hat auch nicht all diese ausgefallenen Optionen ls.
-maxdepth 1
findmacht dies standardmäßig. Dies kann zu Verwirrung führen, wenn man nicht merkt, dass es einen versteckten untergeordneten Ordner gibt, und es kann unter lsbestimmten Umständen vorteilhaft sein, ihn zu verwenden , der standardmäßig keine versteckten Dateien meldet.
Hier ist mein Einzeiler dafür.
file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
set -- wird also nichts anderes getan, als uns darauf $#
Ich habe über diese Antwort viel nachgedacht, besonders angesichts der Dinge , die man nicht analysiert . Zuerst habe ich es versucht
<WARNUNG! Hat nicht funktioniert>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</ WARNUNG! Hat nicht funktioniert>
was funktionierte, wenn es nur einen Dateinamen wie gab
touch $'w\nlf.aa'
aber fehlgeschlagen, wenn ich einen Dateinamen wie diesen gemacht habe
touch $'firstline\n3 and some other\n1\n2\texciting\n86stuff.jpg'
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, lses sei denn, es handelt sich um eine äußerst spezifische Situation.
awk -F"\0" '{print NF-1}' < <(find . -maxdepth 1 -type f -print0) | awk '{sum+=$1}END{print sum}'
Lesbarer:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -print0) | \
awk '{sum+=$1}END{print sum}'
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:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
2) Rekursive Suche. Beachten Sie, dass der Inhalt des -nameParameters möglicherweise geändert werden muss, um ein etwas anderes Verhalten zu erzielen (versteckte Dateien usw.).
awk -F"\0" '{print NF-1}' < \
<(find . -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
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 .
(nicht genug Ruf, um zu kommentieren)
Das ist BUGGY :
ls -1q some_pattern | wc -l
Wenn shopt -s nullglobdies 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 es lsgibt?
Das ist RICHTIG und viel schneller:
shopt -s nullglob; files=(some_pattern); echo ${#files[@]};
Es macht den erwarteten Job.
0.006unter CentOS und 0.083unter Cygwin (falls es mit Vorsicht verwendet wird).
0.000auf CentOS und 0.003auf Cygwin.
Folgendes mache ich immer:
ls log * | awk 'END {print NR}'
awk 'END{print NR}'sollte gleichbedeutend sein mit wc -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 lsParsen 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, countdie die Anzahl der Argumente ausgibt, mit denen sie aufgerufen wurde.
count() { echo $#; }
Nennen Sie es einfach mit dem gewünschten Muster:
count log*
Damit das Ergebnis korrekt ist, wenn das Globbing-Muster nicht übereinstimmt, muss die Shell-Option nullglob(oder failglob- das ist das Standardverhalten bei zsh) zum Zeitpunkt der Erweiterung festgelegt werden. Es kann wie folgt eingestellt werden:
shopt -s nullglob # for sh / bash
setopt nullglob # for zsh
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:
( shopt -s nullglob ; shopt -u failglob ; count log* )
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:
# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
eval "$_count_saved_shopts"
unset _count_saved_shopts
echo $#
}
alias count='
_count_saved_shopts="$(shopt -p nullglob failglob)"
shopt -s nullglob
shopt -u failglob
count'
Als Bonus ist diese Funktion allgemeiner. Zum Beispiel:
count a* b* # count files which match either a* or b*
count $(jobs -ps) # count stopped jobs (sh / bash)
Durch Verwandeln der Funktion in eine Skriptdatei (oder ein gleichwertiges C-Programm), die über PATH aufgerufen werden kann, kann sie auch mit Programmen wie findund zusammengesetzt werden xargs:
find "$FIND_OPTIONS" -exec count {} \+ # count results of a search
ls -1 log* | wc -l
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.
-l, da diesstat(2)für jede Datei erforderlich ist und zum Zwecke des Zählens nichts hinzufügt.