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.