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?

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ć.


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)


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

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

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=-


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.


-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"'


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$

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.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.