Znajdź całkowity rozmiar niektórych plików w gałęzi katalogu

140

Załóżmy, że istnieje katalog przechowywania obrazów, powiedzmy, ./photos/john_doew którym znajduje się wiele podkatalogów, w których znajduje się wiele określonych plików (powiedzmy *.jpg). Jak obliczyć rozmiar tych plików poniżej john_doegałęzi?

Próbowałem du -hs ./photos/john_doe/*/*.jpg, ale pokazuje tylko pojedyncze pliki. Ponadto śledzi to tylko pierwszy poziom zagnieżdżenia john_doekatalogu, podobnie jak john_doe/june/, ale przeskakuje john_doe/june/outrageous/.

Jak więc mógłbym przejść całą gałąź, sumując rozmiar niektórych plików?

mbaitoff
źródło

Odpowiedzi:

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

Jeśli duwymagane jest więcej niż jedno wywołanie, ponieważ lista plików jest bardzo długa, zostanie zgłoszonych wiele sum i trzeba je zsumować.

SHW
źródło
7
znajdź -iname „plik *” -exec du -cb {} + | grep ogółem $ | cut -f1 | wklej -sd + - | bc # zsumowany rozmiar bajtu
Michal Čizmazia
3
Jeśli twój system działa w innym języku, musisz zmienić sumę $ na inne słowo, np. Razem $ po polsku.
Zbyszek
1
Możesz dodać LC_ALL=POSIXjako prefiks, aby zawsze grep dla sumy w ten sposób:LC_ALL=POSIX find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
Sven
2
Jeśli nie używasz -name, zmień grep na, w grep -P "\ttotal$"przeciwnym razie przechwyci on również wszystkie pliki kończące się na „total”.
thdoan
3
@ MichalČizmazia niektóre powłoki (np. Git Bash dla Windows) nie są dostarczane bc, więc oto bardziej przenośne rozwiązanie: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

daje mi całkowite wykorzystanie moich .jpgplików w tym katalogu.

Aby poradzić sobie z wieloma katalogami, prawdopodobnie musiałbyś findjakoś to połączyć .

Przydatne mogą być przykłady poleceń du (obejmuje to również find)

Levon
źródło
2
To nie przechodzi przez bazowe katalogi?
mbaitoff
Jest to łatwiejsze do wpisania niż zaakceptowane rozwiązanie, ale jest tylko w połowie poprawne, nie będzie zawierało obrazów w podkatalogach. Dobrze wiedzieć, czy wszystkie pliki znajdują się w jednym katalogu.
gbmhunter
@gbmhunter Myślę, że jeśli dodasz parametr -R do -ch, dostaniesz również podkatalogi, ponieważ rekurencyjnie przemierza drzewo katalogów. Obecnie nie jestem przy komputerze, aby to wypróbować, aby to potwierdzić.
Levon
1
Nie widzę -Ropcji na man7.org/linux/man-pages/man1/du.1.html . I nie sądzę, aby opcja rekurencyjna pomogła w tym przypadku, ponieważ powłoka wykonuje globalną ekspansję przed przekazaniem argumentów du.
gbmhunter
22

Przede wszystkim potrzebujesz dwóch rzeczy:

du -ch -- **/*.jpg | tail -n 1
Gilles
źródło
bardzo dobra odpowiedź. Prostsze niż użycie find (o ile * lub ** odpowiada strukturze katalogów)
Andre de Miranda
Może także obsługiwać bardzo długie listy plików, a użycie findmoże zwrócić błędne wyniki.
Eric Fournie,
Rozszerzenie nawiasów klamrowych pozwala również na pomiar wielu zestawów symboli wieloznacznych. du -ch -- ./{dir1,dir2}/*.jpglubdu -ch -- ./{prefix1*,prefix2*}.jpg
J.Money
@EricFournie Wystąpił jednak Argument list too longbłąd podczas przetwarzania około 300 000 plików tekstowych.
xtluo
Maksymalną liczbę argumentów dla polecenia (w tym przypadku nazwy plików zwrócone przez rozszerzenie symbolu wieloznacznego) można sprawdzić za pomocą getconf ARG_MAX. Jeśli masz więcej, musisz przetworzyć pliki pojedynczo lub partiami za pomocą pętli for.
Eric Fournie,
17

Ostateczna odpowiedź to:

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

i jeszcze szybsza wersja, nie ograniczona przez pamięć RAM, ale wymaga GNU AWK z obsługą bignum:

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

Ta wersja ma następujące funkcje:

  • wszystkie możliwości findokreślania plików, których szukasz
  • obsługuje miliony plików
    • inne odpowiedzi tutaj są ograniczone przez maksymalną długość listy argumentów
  • spawnuje tylko 3 proste procesy przy minimalnej przepustowości rur
    • wiele odpowiedzi tutaj spawnuje procesy C + N, gdzie C jest pewną stałą, a N jest liczbą plików
  • nie przeszkadza w manipulacji ciągami
    • ta wersja nie wykonuje grepowania ani wyrażeń regularnych
    • dobrze, findrobi proste dopasowanie wieloznaczny nazw plików
  • opcjonalnie formatuje sumę w postaci czytelnej dla człowieka (np. 5.5K, 176.7M, ...)
    • aby to załączyć | numfmt --to=si
Jan Chren - rindeal
źródło
Podoba mi się prostota tej odpowiedzi, chociaż zadziałało to dla mnie tylko wtedy, gdy wprowadziłem spacje po nawiasie otwierającym i przed nawiasem zamykającym. Zastanawiam się, czy to naprawdę będzie obsługiwać „nieskończoną” liczbę plików :)
andyb
1
@andyb dzięki za opinie, spacje wokół nawiasów klamrowych są rzeczywiście wymagane w BASH, używam ZSH, więc tego nie zauważyłem. A liczba plików jest ograniczona dostępną pamięcią RAM w twoim systemie, ponieważ zużycie pamięci bc rośnie powoli wraz ze wzrostem liczby.
Jan Chren - rindeal
8

Odpowiedzi podane do tej pory nie biorą pod uwagę, że lista plików przekazywana z find na du może być tak długa, że ​​find automatycznie dzieli listę na części, co powoduje wielokrotne występowanie total.

Możesz albo grep total(locale!) I podsumować ręcznie, albo użyć innego polecenia. AFAIK są tylko dwa sposoby na uzyskanie całkowitej sumy (w kilobajtach) wszystkich plików znalezionych przez find:
find . -type f -iname '*.jpg' -print0 | xargs -r0 du -a| awk '{sum+=$1} END {print sum}'

Objaśnienie
find . -type f -iname '*.jpg' -print0: Znajdź wszystkie pliki z rozszerzeniem jpg bez względu na wielkość liter (tj. * .Jpg, * .JPG, * .Jpg ...) i wyślij je (zakończone zerem).
xargs -r0 du -a: -r: Xargs wywołałby polecenie nawet bez podania argumentów, co zapobiega -r. -0 oznacza łańcuchy zakończone znakiem null (nie zakończone znakiem nowej linii).
awk '{sum+=$1} END {print sum}': Zsumuj rozmiary plików wyjściowych za pomocą poprzedniego polecenia

Dla porównania, byłby inny sposób
find . -type f -iname '*.jpg' -print0 | du -c --files0-from=-

Jan
źródło
Dodatkowa wskazówka: na moim dysku twardym z 23428 plikami (22323 to obrazy) pierwsza metoda działa 1 sekundę, a druga 3,8 sekundy.
Jan
Zauważ, że oba zakładają system GNU. Pierwszy zakłada, że ​​nazwy plików nie zawierają znaków nowej linii.
Stéphane Chazelas,
Założę się, że du --file0-fromtrwało to dłużej, ponieważ najpierw go uruchomiłeś (efekt buforowania).
Stéphane Chazelas,
Za pomocą można uruchomić xargskilka du -a, więc mogą występować rozbieżności, jeśli istnieją twarde linki.
Stéphane Chazelas,
3

Jeśli lista plików jest zbyt duża, aby nie można jej było przekazać do pojedynczego wywołania du -c, w systemie GNU możesz wykonać:

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

(rozmiar wyrażony liczbą 512 bajtów bloków). Tak dujakby próbował policzyć twarde linki tylko raz. Jeśli nie zależy ci na linkach twardych, możesz uprościć to:

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

Jeśli chcesz rozmiar zamiast użycia dysku, wymień %bsię %s. Rozmiar zostanie wówczas wyrażony w bajtach.

Stéphane Chazelas
źródło
-bash: bc: command not foundCentos - Linux 2.6.32-431.el6.x86_64
yeya
@yeya, wygląda na to, że Twoje wdrożenie CentOS jest zepsute. bcto nie opcjonalne polecenie POSIX.
Stéphane Chazelas,
1

Wspomniane rozwiązania są nieefektywne (wykonanie jest drogie) i wymagają dodatkowej pracy ręcznej, aby zsumować, jeśli lista plików jest długa lub nie działają w systemie Mac OS X. Poniższe rozwiązanie jest bardzo szybkie, powinno działać na każdym systemie i daje całkowitą odpowiedź w GB (usuń a / 1024, jeśli chcesz zobaczyć sumę w MB): find . -iname "*.jpg" -ls |perl -lane '$t += $F[6]; print $t/1024/1024/1024 . " GB"'

hobbydad
źródło
Ani -inameteż nie -lssą standardowe / przenośne, więc też nie będzie działać na żadnym systemie . Nie będzie również działać poprawnie, jeśli istnieją nazwy plików lub cele dowiązań symbolicznych zawierające znaki nowej linii.
Stéphane Chazelas
Zauważ też, że podaje sumę rozmiarów plików, a nie ich użycie na dysku. W przypadku dowiązań symbolicznych podaje rozmiar dowiązań symbolicznych, a nie pliki, na które wskazują.
Stéphane Chazelas
1

Ulepszając świetną odpowiedź SHW, aby działała z dowolnymi lokalizacjami, jak Zbyszek już zauważył w swoim komentarzu:

LC_ALL=C find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
lbo
źródło
1

du oczywiście przegląda hierarchię katalogów, a awk może przeprowadzić filtrowanie, więc coś takiego może być wystarczające:

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

Działa to bez GNU.

GeoffP
źródło
1
Jest to droższe, ponieważ pociąga za sobą statwezwanie do plików, które nie odpowiadają wyszukanemu wzorowi.
Law29
Tylko to rozwiązanie działa na moim komputerze Mac.
Matthias M