Ermitteln der Gesamtgröße bestimmter Dateien in einem Verzeichniszweig

140

Angenommen, es gibt ein Bildspeicherverzeichnis ./photos/john_doe, in dem sich mehrere Unterverzeichnisse befinden, in denen sich beispielsweise viele bestimmte Dateien befinden *.jpg. Wie kann ich eine Gesamtgröße dieser Dateien unterhalb des john_doeZweigs berechnen ?

Ich habe es versucht du -hs ./photos/john_doe/*/*.jpg, aber dies zeigt nur einzelne Dateien. Außerdem verfolgt dies nur die erste Verschachtelungsebene des john_doeVerzeichnisses, john_doe/june/überspringt jedoch john_doe/june/outrageous/.

Wie könnte ich also den gesamten Zweig durchlaufen und die Größe bestimmter Dateien aufsummieren?

mbaitoff
quelle

Antworten:

183
find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$

Wenn mehr als ein Aufruf von duerforderlich ist, weil die Dateiliste sehr lang ist, werden mehrere Summen gemeldet und müssen summiert werden.

SHW
quelle
7
find -iname 'file *' -exec du -cb {} + | grep total $ | cut -f1 | Einfügen -sd + - | bc # summierte
Bytegröße
3
Wenn Ihr System in einer anderen Sprache ausgeführt wird, müssen Sie total $ in ein anderes Wort wie razem $ in Polnisch ändern.
Zbyszek
1
Sie können LC_ALL=POSIXals Präfix hinzufügen, um immer wie folgt nach total LC_ALL=POSIX find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
Sven
2
Wenn Sie nicht verwenden -name, ändern Sie die Option grep in. grep -P "\ttotal$"Andernfalls werden alle Dateien erfasst, die ebenfalls mit "total" enden.
30.
3
@ MichalČizmazia einige Shells (zB Git Bash für Windows) kommen nicht mit bc, so ist hier eine tragbare Lösung:find -name '*.jpg' -type f -exec du -bc {} + | grep total$ | cut -f1 | awk '{ total += $1 }; END { print total }'
thdoan
50
du -ch public_html/images/*.jpg | grep total
20M total

gibt mir die Gesamtnutzung meiner .jpgDateien in diesem Verzeichnis.

Um mit mehreren Verzeichnissen umgehen zu können, müssten Sie dies wahrscheinlich mit etwas anderem kombinieren find.

Möglicherweise sind Beispiele für du-Befehle hilfreich (enthält auch find).

Levon
quelle
2
Durchquert dies nicht die zugrunde liegenden Verzeichnisse?
Mbaitoff
Dies ist einfacher zu tippen als die akzeptierte Lösung, ist aber nur zur Hälfte richtig. Es werden keine Bilder in Unterverzeichnissen aufgenommen. Gut zu wissen, ob sich alle Dateien in einem Verzeichnis befinden.
gbmhunter
@gbmhunter Ich denke, wenn Sie den -R-Parameter zu -ch hinzufügen, erhalten Sie auch die Unterverzeichnisse, da sie den Verzeichnisbaum rekursiv durchlaufen. Ich bin momentan nicht an einem Computer, um es auszuprobieren und zu bestätigen.
Levon
1
Unter man7.org/linux/man-pages/man1/du.1.html wird keine -ROption angezeigt . Und ich glaube nicht, dass eine rekursive Option in diesem Fall helfen würde, da die Shell die Glob-Erweiterung ausführt, bevor die Argumente an übergeben werden du.
Gbmhunter
22

In erster Linie benötigen Sie zwei Dinge:

  • die -cOption du, es zu sagen, um eine Gesamtsumme zu produzieren;
  • entweder **( Aktivierungsanweisungen ) oder find( Beispiel ) oder zum Durchlaufen von Unterverzeichnissen.
du -ch -- **/*.jpg | tail -n 1
Gilles
quelle
sehr gute antwort. Einfacher als find (solange * oder ** der Verzeichnisstruktur entsprechen)
Andre de Miranda
Es kann auch sehr lange Dateilisten verarbeiten, während die Verwendung von findfehlerhaften Ergebnissen führen kann.
Eric Fournie
Durch die Erweiterung der Bash-Klammer können auch mehrere Sätze von Platzhaltern gemessen werden. du -ch -- ./{dir1,dir2}/*.jpgoderdu -ch -- ./{prefix1*,prefix2*}.jpg
J.Money
@EricFournie Allerdings habe ich Argument list too longbeim Verarbeiten von ca. 300k Textdateien Fehler bekommen.
Xtluo
Die maximale Anzahl von Argumenten für einen Befehl (in diesem Fall die von der Platzhaltererweiterung zurückgegebenen Dateinamen) kann mit überprüft werden getconf ARG_MAX. Wenn Sie mehr haben, müssen Sie die Dateien einzeln oder stapelweise mit einer for-Schleife verarbeiten.
Eric Fournie
17

Die ultimative Antwort lautet:

{ find <DIR> -type f -name "*.<EXT>" -printf "%s+"; echo 0; } | bc

und noch schnellere Version, nicht durch RAM begrenzt, sondern benötigt GNU AWK mit Bignum-Unterstützung:

find <DIR> -type f -name "*.<EXT>" -printf "%s\n" | gawk -M '{t+=$1}END{print t}'

Diese Version hat die folgenden Funktionen:

  • Alle Funktionen findzum Angeben der gesuchten Dateien
  • unterstützt Millionen von Dateien
    • andere Antworten sind hier durch die maximale Länge der Argumentliste begrenzt
  • erzeugt nur 3 einfache Prozesse mit minimalem Rohrdurchsatz
    • Viele Antworten hier führen zu C + N-Prozessen, wobei C eine Konstante und N die Anzahl der Dateien ist
  • kümmert sich nicht um die Manipulation von Strings
    • Diese Version macht kein Greifen oder Regexing
    • Nun, findführt einen einfachen Wildcard-Abgleich von Dateinamen durch
  • optional formatiert die Summe in eine für Menschen lesbare Form (zB. 5.5K, 176.7M, ...)
    • das anhängen zu tun | numfmt --to=si
Jan Chren - rindeal
quelle
Ich mag die Einfachheit dieser Antwort, obwohl sie nur bei mir funktioniert hat, als ich nach der öffnenden und vor der schließenden Klammer Leerzeichen einführte. Ich frage mich, ob es wirklich eine unendliche Anzahl von Dateien unterstützen wird :)
andyb
1
@andyb danke für die Rückmeldung, die Leerzeichen um Klammern sind in der Tat in BASH erforderlich. Ich verwende ZSH, deshalb habe ich das nicht bemerkt. Und die Anzahl der Dateien ist durch den verfügbaren RAM auf Ihrem System begrenzt, da die Speichernutzung von bc langsam mit dem
Einfließen
8

Die bisher gegebenen Antworten berücksichtigen nicht, dass die von find an du übergebene Dateiliste so lang sein kann, dass find die Liste automatisch in Blöcke aufteilt, was zu mehreren Vorkommen von führt total.

Sie können entweder grep total(Gebietsschema!) Und manuell zusammenfassen oder einen anderen Befehl verwenden. AFAIK: Es gibt nur zwei Möglichkeiten, eine Gesamtsumme (in Kilobyte) aller durch find gefundenen Dateien zu erhalten:
find . -type f -iname '*.jpg' -print0 | xargs -r0 du -a| awk '{sum+=$1} END {print sum}'

Erläuterung
find . -type f -iname '*.jpg' -print0: Suchen Sie alle Dateien mit der Erweiterung jpg, unabhängig von der Groß- und Kleinschreibung (z. B. * .jpg, * .JPG, * .Jpg ...), und geben Sie sie aus (nullterminiert).
xargs -r0 du -a: -r: Xargs würde den Befehl auch ohne Argumente aufrufen, was -r verhindert. -0 bedeutet nullterminierte Zeichenfolgen (nicht mit Zeilenvorschub abgeschlossen).
awk '{sum+=$1} END {print sum}': Fassen Sie die vom vorherigen Befehl ausgegebenen Dateigrößen zusammen

Und als Referenz wäre der andere Weg
find . -type f -iname '*.jpg' -print0 | du -c --files0-from=-

Jan
quelle
Zusätzlicher Hinweis: Auf meiner Festplatte mit 23428 Dateien (22323 sind Bilder) dauert die erste Methode 1 Sekunde, während die zweite 3,8 Sekunden dauert.
Jan
Beachten Sie, dass beide ein GNU-System voraussetzen. Der erste geht davon aus, dass Dateinamen keine Zeilenumbrüche enthalten.
Stéphane Chazelas
Ich wette, das du --file0-fromhat länger gedauert, weil du es zuerst ausgeführt hast (Caching-Effekt).
Stéphane Chazelas
Mit xargskönnen mehrere ausgeführt du -awerden, sodass es bei harten Links zu Unstimmigkeiten kommen kann.
Stéphane Chazelas
3

Wenn die Liste der Dateien zu groß ist, als dass sie du -cauf einem GNU-System an einen einzelnen Aufruf von nicht übergeben werden kann, haben Sie folgende Möglichkeiten :

find . -iname '*.jpg' -type f -printf '%b\t%D:%i\n' |
  sort -u | cut -f1 | paste -sd+ - | bc

(Größe ausgedrückt in 512-Byte-Blöcken). Wie dues versucht, feste Verbindungen nur einmal zu zählen. Wenn Sie sich nicht für Hardlinks interessieren, können Sie es vereinfachen, um:

(printf 0; find . -iname '*.jpg' -type f -printf +%b) | bc

Wenn Sie die Größe anstelle der Festplattenbelegung verwenden möchten, ersetzen Sie sie %bdurch %s. Die Größe wird dann in Bytes angegeben.

Stéphane Chazelas
quelle
-bash: bc: command not foundCentos - Linux 2.6.32-431.el6.x86_64
yeya
@yeya, klingt wie Ihre CentOS-Bereitstellung ist kaputt. bcist ein nicht optionaler POSIX-Befehl.
Stéphane Chazelas
1

Die bisher genannten Lösungen sind ineffizient (exec ist teuer) und erfordern zusätzliche manuelle Arbeit, wenn die Dateiliste lang ist oder sie unter Mac OS X nicht funktionieren. Die folgende Lösung ist sehr schnell und sollte auf jedem System funktionieren ergibt die Gesamtantwort in GB (entferne a / 1024, wenn du die Gesamtantwort in MB sehen willst): find . -iname "*.jpg" -ls |perl -lane '$t += $F[6]; print $t/1024/1024/1024 . " GB"'

Hobbydad
quelle
Weder -inamenoch -lssind Standard / tragbar, so dass es nicht auf jedem System arbeiten entweder. Es wird auch nicht richtig funktionieren, wenn es Dateinamen oder Symlink-Ziele gibt, die Zeilenumbrüche enthalten.
Stéphane Chazelas
Beachten Sie auch, dass es die Summe der Dateigrößen gibt, nicht deren Datenträgerverwendung. Bei Symlinks wird die Größe der Symlinks angegeben, nicht die Dateien, auf die sie verweisen.
Stéphane Chazelas
1

Die großartige Antwort von SHW dahingehend verbessern, dass sie mit jedem Gebietsschema funktioniert, wie Zbyszek bereits in seinem Kommentar ausgeführt hat:

LC_ALL=C find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
lbo
quelle
1

du durchquerst natürlich die Verzeichnishierarchie und awk kann die Filterung durchführen, so dass so etwas ausreichend sein kann:

du -ak | awk 'BEGIN {sum=0} /\.jpg$/ {sum+=$1} END {print sum}'

Dies funktioniert ohne GNU.

GeoffP
quelle
1
Dies ist teurer, da statDateien aufgerufen werden müssen, die nicht dem gesuchten Muster entsprechen.
Law29
Nur diese Lösung funktioniert auf meinem Mac.
Matthias M