Encontre o tamanho total de determinados arquivos em uma ramificação de diretório

140

Suponha que exista um diretório de armazenamento de imagens ./photos/john_doe, no qual existem vários subdiretórios, nos quais residem muitos arquivos (digamos *.jpg). Como posso calcular um tamanho de resumo desses arquivos abaixo da john_doeramificação?

Eu tentei du -hs ./photos/john_doe/*/*.jpg, mas isso mostra apenas arquivos individuais. Além disso, isso rastreia apenas o primeiro nível de aninhamento do john_doediretório, como john_doe/june/, mas pula john_doe/june/outrageous/.

Então, como eu poderia percorrer todo o ramo, resumindo o tamanho de certos arquivos?

mbaitoff
fonte

Respostas:

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

Se mais de uma chamada de dufor necessária porque a lista de arquivos é muito longa, múltiplos totais serão relatados e precisarão ser somados.

SHW
fonte
7
encontre -iname 'arquivo *' -exec du -cb {} + | total grep $ | corte -f1 | colar -sd + - | bc # tamanho total de bytes
Michal Čizmazia 15/07/2015
3
Se o seu sistema funcionar em outro idioma, você precisará alterar o total de $ para outra palavra como razem $ em polonês.
precisa
1
Você pode adicionar LC_ALL=POSIXcomo prefixo para sempre grep para total como este:LC_ALL=POSIX find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
Sven
2
Se você não estiver usando -name, altere o grep para grep -P "\ttotal$"senão ele captura todos os arquivos que terminam com "total" também.
thdoan
3
@ MichalČizmazia algumas conchas (por exemplo, Git Bash para Windows) não vêm com bc, por isso aqui é uma solução mais portátil: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

fornece o uso total dos meus .jpgarquivos neste diretório.

Para lidar com vários diretórios, você provavelmente teria que combinar isso de findalguma forma.

Você pode achar exemplos de comandos du úteis (também inclui find)

Levon
fonte
2
Isso não atravessa os diretórios subjacentes?
mbaitoff
É mais fácil digitar do que a solução aceita, mas é apenas parcialmente certo, pois não inclui imagens em subdiretórios. É bom saber se todos os arquivos estão em um diretório.
gbmhunter 29/08
@gbmhunter Acho que se você adicionar o parâmetro -R a -ch, também receberá os subdiretórios, pois ele percorre recursivamente a árvore de diretórios. No momento, não estou em um computador para testá-lo, mas para confirmar.
Levon
1
Não vejo uma -Ropção em man7.org/linux/man-pages/man1/du.1.html . E eu não acho que uma opção recursiva ajudaria nesse caso, porque o shell está fazendo a expansão global antes de passar os argumentos para du.
gbmhunter 30/08
22

Primeiramente, você precisa de duas coisas:

du -ch -- **/*.jpg | tail -n 1
Gilles
fonte
resposta muito boa. Mais simples do que usando find (desde * ou ** corresponde a estrutura de diretórios)
Andre de Miranda
Ele também pode lidar com listas muito longas de arquivos, enquanto o uso findpode retornar resultados incorretos.
Eric Fournie 19/10/16
A expansão do bash brace também permite medir vários conjuntos de curingas. du -ch -- ./{dir1,dir2}/*.jpgoudu -ch -- ./{prefix1*,prefix2*}.jpg
J.Money 23/07
@ EricFournie No entanto, recebi um Argument list too longerro ao processar cerca de 300k arquivos de texto.
xtluo
O número máximo de argumentos para um comando (nesse caso, os nomes de arquivo retornados pela expansão de curinga) pode ser verificado getconf ARG_MAX. Se você tiver mais, precisará processar os arquivos um por um ou em lotes com um loop for.
Eric Fournie
17

A resposta final é:

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

e versão ainda mais rápida, não limitada pela RAM, mas que requer o GNU AWK com suporte a bignum:

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

Esta versão possui os seguintes recursos:

  • todos os recursos findpara especificar os arquivos que você está procurando
  • suporta milhões de arquivos
    • outras respostas aqui são limitadas pelo comprimento máximo da lista de argumentos
  • gera apenas 3 processos simples com um rendimento de tubo mínimo
    • aqui muitas respostas geram processos C + N, onde C é uma constante e N é o número de arquivos
  • não se incomoda com a manipulação de strings
    • esta versão não faz grepping ou regexing
    • bem, findfaz uma correspondência curinga simples de nomes de arquivos
  • opcionalmente formata a soma para uma forma legível (por exemplo,. 5.5K, 176.7M, ...)
    • para fazer isso acrescentar | numfmt --to=si
Jan Chren - rindeal
fonte
Gosto da simplicidade desta resposta, embora só tenha funcionado para mim quando introduzi espaços após a chave de abertura e antes da chave de fechamento. Eu me pergunto se ele realmente suportará um número 'infiinte' de arquivos :)
andyb
1
@andyb obrigado pelo feedback, os espaços ao redor dos aparelhos são realmente necessários no BASH, estou usando o ZSH, então não percebi isso. E o número de arquivos é limitada pela RAM disponível em seu sistema, como o uso de memória do bc cresce lentamente como o fluxo de números.
Jan Chren - rindeal
8

As respostas dadas até agora não levam em consideração que a lista de arquivos passada de find para du pode ser tão longa que a find divide automaticamente a lista em partes, resultando em várias ocorrências de total.

Você pode grep total(localidade!) E resumir manualmente, ou usar um comando diferente. No AFAIK, existem apenas duas maneiras de obter um total geral (em kilobytes) de todos os arquivos encontrados pela localização:
find . -type f -iname '*.jpg' -print0 | xargs -r0 du -a| awk '{sum+=$1} END {print sum}'

Explicação
find . -type f -iname '*.jpg' -print0: Encontre todos os arquivos com a extensão jpg, independentemente do caso (por exemplo, * .jpg, * .JPG, * .Jpg ...) e os produza (terminação nula).
xargs -r0 du -a: -r: Xargs chamaria o comando mesmo sem argumentos passados, o que -r impede. -0 significa cadeias terminadas em nulo (não nova linha finalizada).
awk '{sum+=$1} END {print sum}': Resuma os tamanhos de arquivo gerados pelo comando anterior

E, para referência, o outro caminho seria
find . -type f -iname '*.jpg' -print0 | du -c --files0-from=-

Jan
fonte
Dica adicional: No meu HDD com 23428 arquivos (sendo 22323 imagens), o primeiro método executa 1 segundo enquanto o segundo executa 3,8 segundos.
Jan
Note que ambos assumem um sistema GNU. O primeiro assume que os nomes dos arquivos não contêm caracteres de nova linha.
Stéphane Chazelas
Aposto que du --file0-fromdemorou mais tempo porque você o executou primeiro (efeito de cache).
Stéphane Chazelas
Com xargs, vários du -apodem ser executados, portanto, você pode ter discrepâncias se houver links físicos.
Stéphane Chazelas
3

Se a lista de arquivos for muito grande e não puder ser passada para uma única chamada de du -c, em um sistema GNU, você poderá:

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

(tamanho expresso em número de blocos de 512 bytes). Como duse tentasse contar links físicos apenas uma vez. Se você não se importa com links físicos, pode simplificá-lo para:

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

Se você deseja o tamanho em vez do uso do disco, substitua %bpor %s. O tamanho será então expresso em bytes.

Stéphane Chazelas
fonte
-bash: bc: command not foundCentos - Linux 2.6.32-431.el6.x86_64
yeya
@ yeya, parece que sua implantação do CentOS está interrompida. bcé um comando POSIX não opcional.
Stéphane Chazelas
1

As soluções mencionadas até o momento são ineficientes (o custo do executivo) e exigem trabalho manual adicional para somar se a lista de arquivos é longa ou não funciona no Mac OS X. A solução a seguir é muito rápida, deve funcionar em qualquer sistema e gera a resposta total em GB (remova a / 1024 se desejar ver o total em MB): find . -iname "*.jpg" -ls |perl -lane '$t += $F[6]; print $t/1024/1024/1024 . " GB"'

hobbydad
fonte
Nem -inamenem -lssão padrão / portátil, por isso não vai funcionar em qualquer sistema também. Também não funcionará corretamente se houver nomes de arquivos ou destinos de link simbólico que contenham caracteres de nova linha.
Stéphane Chazelas
Observe também que ele fornece a soma dos tamanhos dos arquivos, não do uso do disco. Para links simbólicos, fornece o tamanho dos links simbólicos, não os arquivos para os quais eles apontam.
Stéphane Chazelas
1

Melhorando a ótima resposta do SHW para fazê-lo funcionar com qualquer localidade, como Zbyszek já apontou em seu comentário:

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

du naturalmente percorre a hierarquia de diretórios e o awk pode executar a filtragem, portanto algo como isto pode ser suficiente:

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

Isso funciona sem o GNU.

GeoffP
fonte
1
Isso é mais caro, pois envolve uma statchamada para arquivos que não correspondem ao padrão pesquisado.
precisa
Somente esta solução funciona no meu mac.
Matthias M