Ich muss alle Unterverzeichnisse überprüfen und angeben, wie viele Dateien sie enthalten (ohne weitere Rekursion):
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Ich muss alle Unterverzeichnisse überprüfen und angeben, wie viele Dateien sie enthalten (ohne weitere Rekursion):
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Antworten:
Dies geschieht auf sichere und tragbare Weise. Es wird nicht durch seltsame Dateinamen verwechselt.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
Beachten Sie, dass zuerst die Anzahl der Dateien und dann der Verzeichnisname in einer separaten Zeile ausgegeben werden. Wenn Sie das OP-Format beibehalten möchten, müssen Sie weitere Formatierungen vornehmen, z
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
Wenn Sie bestimmte Unterverzeichnisse haben, an denen Sie interessiert sind, können Sie diese ersetzen *
.
Warum ist das sicher? (und daher script-würdig)
Dateinamen können außer beliebige Zeichen enthalten /
. Es gibt einige Zeichen, die entweder von der Shell oder von den Befehlen speziell behandelt werden. Dazu gehören Leerzeichen, Zeilenumbrüche und Bindestriche.
Die Verwendung des for f in *
Konstrukts ist eine sichere Methode, um jeden Dateinamen abzurufen, unabhängig davon, was er enthält.
Sobald Sie den Dateinamen in einer Variablen haben, müssen Sie noch Dinge wie vermeiden find $f
. Wenn $f
der Dateiname enthalten ist -test
, find
würde ich mich über die Option beschweren, die Sie gerade angegeben haben. Die Möglichkeit, dies zu vermeiden, besteht in der Verwendung ./
vor dem Namen. Auf diese Weise hat es die gleiche Bedeutung, aber es beginnt nicht mehr mit einem Bindestrich.
Zeilenumbrüche und Leerzeichen sind ebenfalls ein Problem. Wenn $f
"Hallo, Kumpel" als Dateiname enthalten ist find ./$f
, ist find ./hello, buddy
. Du sagst find
zu sehen ./hello,
und buddy
. Wenn diese nicht existieren, wird es sich beschweren und niemals hineinschauen ./hello, buddy
. Dies lässt sich leicht vermeiden - verwenden Sie Anführungszeichen um Ihre Variablen.
Schließlich können Dateinamen Zeilenumbrüche enthalten, sodass das Zählen von Zeilenumbrüchen in einer Liste von Dateinamen nicht funktioniert. Für jeden Dateinamen mit einer neuen Zeile erhalten Sie eine zusätzliche Anzahl. Um dies zu vermeiden, zählen Sie keine Zeilenumbrüche in einer Dateiliste. Zählen Sie stattdessen Zeilenumbrüche (oder andere Zeichen), die eine einzelne Datei darstellen. Deshalb hat der find
Befehl einfach -exec echo \;
und nicht -exec echo {} \;
. Ich möchte nur eine einzige neue Zeile drucken, um die Dateien zu zählen.
-mindepth 1
-printf '\n'
anstelle von verwenden -exec echo
.
-printf
, aber nicht, wenn Sie möchten, dass es zum Beispiel unter FreeBSD funktioniert.
Unter der Annahme, dass Sie nach einer Standard-Linux-Lösung suchen, können Sie dies relativ einfach erreichen mit find
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Wobei find
die beiden angegebenen Unterverzeichnisse durchlaufen werden, bis zu einem -maxdepth
von 1, was eine weitere Rekursion verhindert und nur Dateien ( -type f
) meldet, die durch Zeilenumbrüche getrennt sind. Das Ergebnis wird dann weitergeleitet, wc
um die Anzahl dieser Zeilen zu zählen.
find . -maxdepth 1 -type d
Ausgabe kombinieren ?
find $dirs ...
(b), wenn sie sich ausschließlich in dem einen übergeordneten Verzeichnis befinden, Glob aus diesem Verzeichnis,find */ ...
-exec echo
Ihren Suchbefehl hinzu - auf diese Weise wird der Dateiname nicht wiedergegeben, sondern nur eine neue Zeile.
Mit „ohne Rekursion“ meinen Sie, dass Sie, wenn Sie directoryName1
Unterverzeichnisse haben, die Dateien in den Unterverzeichnissen nicht zählen möchten? In diesem Fall können Sie alle regulären Dateien in den angegebenen Verzeichnissen zählen:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
Beachten Sie, dass der -f
Test zwei Funktionen ausführt: Er testet, ob der von einem der obigen Globs gefundene Eintrag eine reguläre Datei ist, und er testet, ob der Eintrag ein Treffer war (wenn einer der Globs mit nichts übereinstimmt, bleibt das Muster wie es ist¹). Wenn Sie alle Einträge in den angegebenen Verzeichnissen unabhängig von ihrem Typ zählen möchten, ersetzen Sie -f
durch -e
.
Ksh hat eine Möglichkeit, Muster mit Punktdateien abzugleichen und eine leere Liste zu erstellen, falls keine Datei mit einem Muster übereinstimmt. In ksh können Sie also reguläre Dateien wie folgt zählen:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
oder alle Dateien einfach so:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
Bash hat verschiedene Möglichkeiten, dies zu vereinfachen. So zählen Sie reguläre Dateien:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
So zählen Sie alle Dateien:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
Wie immer ist es in zsh noch einfacher. So zählen Sie reguläre Dateien:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
Wechseln Sie (DN.)
zu, (DN)
um alle Dateien zu zählen.
¹ Beachten Sie, dass jedes Muster mit sich selbst übereinstimmt. Andernfalls sind die Ergebnisse möglicherweise nicht korrekt (wenn Sie z. B. Dateien zählen, die mit einer Ziffer beginnen, können Sie dies nicht einfach tun, for x in [0-9]*; do if [ -f "$x" ]; then …
da möglicherweise eine Datei aufgerufen wird [0-9]foo
).
Basierend auf einer Zählung Skript , Shawn Antwort und ein Bash Trick , um sicherzustellen , dass auch die Dateinamen mit Zeilenumbrüchen in verwertbarer Form in einer einzigen Zeile gedruckt werden:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %q
Gibt eine in Anführungszeichen gesetzte Version einer Zeichenfolge aus, d. h. eine einzeilige Zeichenfolge, die Sie in ein Bash-Skript einfügen können, um sie als Literalzeichenfolge mit (potenziellen) Zeilenumbrüchen und anderen Sonderzeichen zu interpretieren. Siehe zum Beispiel echo -n $'\tfoo\nbar'
vs printf %q $'\tfoo\nbar'
.
Der find
Befehl druckt einfach ein einzelnes Zeichen für jede Datei und zählt diese statt der Zeilen.
Hier ist ein „Brute-Force“ -ish Weg , um Ihr Ergebnis zu erhalten, mit find
, echo
, ls
, wc
, xargs
und awk
.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
wenn Bash dies tut?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
: Zählen Sie für alle Verzeichnisse die Anzahl der Einträge in diesem Verzeichnis (einschließlich versteckter.
..