Encuentra el tamaño total de ciertos archivos dentro de una rama de directorio

140

Supongamos que hay un directorio de almacenamiento de imágenes, digamos, ./photos/john_doedentro del cual hay múltiples subdirectorios, donde residen ciertos archivos (digamos *.jpg). ¿Cómo puedo calcular un tamaño de resumen de esos archivos debajo de la john_doerama?

Lo intenté du -hs ./photos/john_doe/*/*.jpg, pero esto muestra solo archivos individuales. Además, esto solo rastrea el primer nivel de anidamiento del john_doedirectorio, como john_doe/june/, pero omite john_doe/june/outrageous/.

Entonces, ¿cómo podría atravesar toda la rama, resumiendo el tamaño de ciertos archivos?

mbaitoff
fuente

Respuestas:

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

Si duse requiere más de una invocación de porque la lista de archivos es muy larga, se informarán varios totales y deberán sumarse.

SHW
fuente
77
find -iname 'archivo *' -exec du -cb {} + | grep total $ | corte -f1 | pegar -sd + - | bc # tamaño de byte sumado
Michal Čizmazia
3
Si su sistema funciona en otro idioma, entonces necesita cambiar el total de $ a otra palabra como razem $ en polaco.
Zbyszek
1
Puede agregar LC_ALL=POSIXcomo prefijo a grep para un total como este:LC_ALL=POSIX find ./photos/john_doe -type f -name '*.jpg' -exec du -ch {} + | grep total$
Sven
2
Si no está usando -name, cambie el grep a grep -P "\ttotal$"o de lo contrario capturará todos los archivos que terminen en "total" también.
Thdoan
3
@ MichalČizmazia algunas conchas (por ejemplo, Git Bash para Windows) no vienen con bc, por lo que aquí es una solución más 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

me da el uso total de mis .jpgarchivos en este directorio.

Para lidiar con múltiples directorios, probablemente tenga que combinar esto de findalguna manera.

Puede encontrar ejemplos de comandos du útiles (también incluye find)

Levon
fuente
2
Esto no atraviesa los directorios subyacentes?
mbaitoff
Esto es más fácil de escribir que la solución aceptada, pero es solo medio correcto, no incluirá imágenes en subdirectorios. Es bueno saber si todos los archivos están en un directorio.
gbmhunter
@gbmhunter Creo que si agrega el parámetro -R a -ch también obtendrá los subdirectorios a medida que atraviesa recursivamente el árbol de directorios. Actualmente no estoy en una computadora para probarlo, aunque para confirmar.
Levon
1
No veo una -Ropción en man7.org/linux/man-pages/man1/du.1.html . Y no creo que una opción recursiva ayude en este caso porque el shell está haciendo la expansión global antes de pasarle los argumentos du.
gbmhunter
22

Principalmente, necesitas dos cosas:

du -ch -- **/*.jpg | tail -n 1
Gilles
fuente
Muy buena respuesta. Más simple que usar find (siempre que * o ** coincida con la estructura del directorio)
Andre de Miranda
También puede manejar listas muy largas de archivos, mientras que el uso findpuede devolver resultados erróneos.
Eric Fournie
la expansión bash brace también permite medir múltiples conjuntos de comodines. du -ch -- ./{dir1,dir2}/*.jpgodu -ch -- ./{prefix1*,prefix2*}.jpg
J.Money
@EricFournie Sin embargo, recibí un Argument list too longerror al procesar aproximadamente 300k archivos de texto.
xtluo
Se puede consultar el número máximo de argumentos para un comando (en este caso, los nombres de archivo devueltos por la expansión de comodines) getconf ARG_MAX. Si tiene más, deberá procesar los archivos uno por uno o por lotes con un bucle for.
Eric Fournie
17

La respuesta final es:

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

e incluso una versión más rápida, no limitada por RAM, pero eso requiere GNU AWK con soporte bignum:

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

Esta versión tiene las siguientes características:

  • todas las capacidades de findespecificar los archivos que está buscando
  • soporta millones de archivos
    • otras respuestas aquí están limitadas por la longitud máxima de la lista de argumentos
  • genera solo 3 procesos simples con un rendimiento mínimo de tubería
    • muchas respuestas aquí generan procesos C + N, donde C es algo constante y N es el número de archivos
  • no se molesta con la manipulación de cuerdas
    • esta versión no hace grepping o regexing
    • bueno, findhace una simple combinación de comodines de nombres de archivo
  • formatos opcionalmente la suma en una forma legible (por ejemplo. 5.5K, 176.7M, ...)
    • para hacer eso anexar | numfmt --to=si
Jan Chren - rindeal
fuente
Me gusta la simplicidad de esta respuesta, aunque solo funcionó para mí cuando introduje espacios después de la llave de apertura y antes de la llave de cierre. Sin embargo, me pregunto si realmente admitirá un número 'infiinte' de archivos :)
andyb
1
@andyb gracias por los comentarios, los espacios alrededor de las llaves son realmente necesarios en BASH, estoy usando ZSH, así que no me di cuenta. Y la cantidad de archivos está limitada por la RAM disponible en su sistema ya que el uso de la memoria de bc crece lentamente a medida que los números fluyen.
Jan Chren - rindeal
8

Las respuestas dadas hasta ahora no tienen en cuenta que la lista de archivos pasada de find a du puede ser tan larga que find divide automáticamente la lista en fragmentos, lo que resulta en múltiples ocurrencias de total.

Puede grep total(¡locale!) Y resumir manualmente, o usar un comando diferente. AFAIK solo hay dos formas de obtener un gran total (en kilobytes) de todos los archivos encontrados por find:
find . -type f -iname '*.jpg' -print0 | xargs -r0 du -a| awk '{sum+=$1} END {print sum}'

Explicación
find . -type f -iname '*.jpg' -print0: Encuentre todos los archivos con la extensión jpg sin importar el caso (es decir, * .jpg, * .JPG, * .Jpg ...) y envíelos (terminados en nulo).
xargs -r0 du -a: -r: Xargs llamaría al comando incluso sin pasar argumentos, lo que -r evita. -0 significa cadenas terminadas en nulo (no terminadas en nueva línea).
awk '{sum+=$1} END {print sum}': Resuma los tamaños de archivo generados por el comando anterior

Y para referencia, la otra forma sería
find . -type f -iname '*.jpg' -print0 | du -c --files0-from=-

ene
fuente
Sugerencia adicional: en mi HDD con 23428 archivos (22323 son imágenes), el primer método funciona 1 segundo, mientras que el segundo ejecuta 3,8 segundos.
Ene
Tenga en cuenta que ambos asumen un sistema GNU. El primero supone que los nombres de archivo no contienen caracteres de nueva línea.
Stéphane Chazelas
Apuesto a que du --file0-fromtomó más tiempo porque lo ejecutó primero (efecto de almacenamiento en caché).
Stéphane Chazelas
Con xargs, du -ase pueden ejecutar varios , por lo que puede tener discrepancias si hay enlaces duros.
Stéphane Chazelas
3

Si la lista de archivos es demasiado grande y no se puede pasar a una sola invocación de du -c, en un sistema GNU, puede hacer lo siguiente:

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

(tamaño expresado en número de bloques de 512 bytes). Al igual duque trata de contar enlaces duros solo una vez. Si no le interesan los enlaces duros, puede simplificarlo a:

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

Si desea el tamaño en lugar del uso del disco, reemplácelo %bcon %s. El tamaño se expresará en bytes.

Stéphane Chazelas
fuente
-bash: bc: command not foundCentos - Linux 2.6.32-431.el6.x86_64
yeya
@yeya, parece que su implementación de CentOS está rota. bces un comando POSIX no opcional.
Stéphane Chazelas
1

Las soluciones mencionadas hasta ahora son ineficientes (exec es costoso) y requieren trabajo manual adicional para sumar si la lista de archivos es larga o no funcionan en Mac OS X. La siguiente solución es muy rápida, debería funcionar en cualquier sistema, y produce la respuesta total en GB (elimine a / 1024 si desea ver el total en MB): find . -iname "*.jpg" -ls |perl -lane '$t += $F[6]; print $t/1024/1024/1024 . " GB"'

hobbydad
fuente
Ni -inametampoco -lsson estándar / portátiles, por lo que tampoco funcionará en ningún sistema . Tampoco funcionará correctamente si hay nombres de archivo u objetivos de enlace simbólico que contengan caracteres de nueva línea.
Stéphane Chazelas
También tenga en cuenta que da la suma de los tamaños de archivo, no su uso de disco. Para los enlaces simbólicos, proporciona el tamaño de los enlaces simbólicos, no los archivos a los que apuntan.
Stéphane Chazelas
1

Mejorar la excelente respuesta de SHW para que funcione con cualquier configuración regional, como Zbyszek ya señaló en su comentario:

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

du atraviesa naturalmente la jerarquía de directorios y awk puede realizar el filtrado, por lo que algo como esto puede ser suficiente:

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

Esto funciona sin GNU.

GeoffP
fuente
1
Esto es más costoso ya que implica una statllamada para archivos que no corresponden al patrón buscado.
Law29
Solo esta solución funciona en mi mac.
Matthias M