Generar distribución de tamaños de archivo desde el símbolo del sistema

16

Tengo un sistema de archivos que tiene un par de millones de archivos y me gustaría ver una distribución de tamaños de archivos de forma recursiva en un directorio particular. Siento que esto es totalmente factible con un poco de bash / awk fu, pero podría usar una mano. Básicamente me gustaría algo como lo siguiente:

1KB: 4123
2KB: 1920
4KB: 112
...
4MB: 238
8MB: 328
16MB: 29138
Count: 320403345

Siento que esto no debería ser tan malo dado un bucle y algún tamaño de archivo log2 condicional, pero parece que no puedo llegar allí.

Pregunta relacionada: ¿Cómo puedo encontrar archivos que sean más grandes / más pequeños que x bytes? .

notpeter
fuente

Respuestas:

21

Esto parece funcionar bastante bien:

find . -type f -print0 | xargs -0 ls -l | awk '{size[int(log($5)/log(2))]++}END{for (i in size) printf("%10d %3d\n", 2^i, size[i])}' | sort -n

Su salida se ve así:

         0   1
         8   3
        16   2
        32   2
        64   6
       128   9
       256   9
       512   6
      1024   8
      2048   7
      4096  38
      8192  16
     16384  12
     32768   7
     65536   3
    131072   3
    262144   3
    524288   6
   2097152   2
   4194304   1
  33554432   1
 134217728   4
donde el número de la izquierda es el límite inferior de un rango de ese valor al doble de ese valor y el número de la derecha es el número de archivos en ese rango.

garyjohn
fuente
Edité su respuesta para usar find en lugar de ls para que fuera recursiva y no contara ningún directorio. ¿Alguien quiere echar un vistazo a embellecer la salida de la columna de la izquierda?
notpeter
Pero la pregunta original era sobre "la distribución de tamaños de archivo en un directorio particular", por lo que no está bien cambiar el lsa find. Lo estoy volviendo a poner como estaba.
garyjohn
@notpeter: Lo siento, no te reconocí como el autor de la pregunta. Cambié mi respuesta para que buscara recursivamente. Sin embargo, en mi sistema, usar xargses significativamente más rápido que -exec, así que usé ese método.
garyjohn
1
Sin preocupaciones. Ahora podemos simplemente eliminar nuestros comentarios, se pretende que siempre fue la respuesta correcta. ;)
notpeter
14

Basado en la respuesta de garyjohn, aquí hay una línea, que también formatea la salida a legible para humanos:

find . -type f -print0 | xargs -0 ls -l | awk '{ n=int(log($5)/log(2)); if (n<10) { n=10; } size[n]++ } END { for (i in size) printf("%d %d\n", 2^i, size[i]) }' | sort -n | awk 'function human(x) { x[1]/=1024; if (x[1]>=1024) { x[2]++; human(x) } } { a[1]=$1; a[2]=0; human(a); printf("%3d%s: %6d\n", a[1],substr("kMGTEPYZ",a[2]+1,1),$2) }'

Aquí está la versión ampliada de la misma:

find . -type f -print0                                                   \ 
 | xargs -0 ls -l                                                        \
 | awk '{ n=int(log($5)/log(2));                                         \
          if (n<10) n=10;                                                \
          size[n]++ }                                                    \
      END { for (i in size) printf("%d %d\n", 2^i, size[i]) }'           \
 | sort -n                                                               \ 
 | awk 'function human(x) { x[1]/=1024;                                  \
                            if (x[1]>=1024) { x[2]++;                    \
                                              human(x) } }               \
        { a[1]=$1;                                                       \ 
          a[2]=0;                                                        \
          human(a);                                                      \
          printf("%3d%s: %6d\n", a[1],substr("kMGTEPYZ",a[2]+1,1),$2) }' 

En el primero awk, definí un tamaño de archivo mínimo para recopilar todos los archivos de menos de 1 kb en un solo lugar. En el segundo awk, la función human(x)se define para crear un tamaño legible por humanos. Esta parte se basa en una de las respuestas aquí: /unix/44040/a-standard-tool-to-convert-a-byte-count-into-human-kib-mib-etc -like-du-ls1

La salida de muestra se ve así:

  1k:    335
  2k:     16
 32k:      5
128k:     22
  1M:     54
  2M:     11
  4M:     13
  8M:      3
dzsuz87
fuente
2

Prueba esto:

find . -type f -exec ls -lh {} \; | 
 gawk '{match($5,/([0-9.]+)([A-Z]+)/,k); if(!k[2]){print "1K"} \
        else{printf "%.0f%s\n",k[1],k[2]}}' | 
sort | uniq -c | sort -hk 2 

SALIDA

 38 1K
 14 2K
  1 30K
  2 62K
  12 2M
  2 3M
  1 31M
  1 46M
  1 56M
  1 75M
  1 143M
  1 191M
  1 246M
  1 7G

EXPLICACIÓN

  • find . -type f -exec ls -lh {} \;: Bastante simple, encontrar archivos en el directorio actual y ejecutar ls -lhen ellos

  • match($5,/([0-9.]+)([A-Z]+)/,k);: esto extraerá el tamaño del archivo y guardará cada coincidencia en la matriz k.

  • if(!k[2]){print "1K"}: si k[2]no está definido, el tamaño del archivo es <1K. Como me imagino que no le importan esos tamaños tan pequeños, el script se imprimirá 1Kpara todos los archivos cuyo tamaño es <= 1K.

  • else{printf "%.0f%s\n",k[1],k[2]} : si el archivo es mayor a 1K, redondea el tamaño del archivo al entero más cercano e imprime junto con su modificador (K, M o G).

  • sort | uniq -c : cuenta las ocurrencias de cada línea (tamaño de archivo) impresa.

  • sort -hk 2: ordenar según el segundo campo en formato legible por humanos. De esta manera, 7Gse ordena después 8M.

terdon
fuente
Agradezco las explicaciones, creo que es útil para las personas que intentan resolverlo. Dicho esto, su secuencia de comandos no funciona para mí por dos razones 1) Mi GNU LS es antigua y, por lo tanto, proporciona una salida de tamaño legible por humanos diferente para 'ls -lh' (bytes no K / M / G / T) y 2) porque Hay demasiados cubos. Con tamaños de archivo entre 1K y 1G hay 2000 cubos, la mitad de los cuales son 1KB y la mitad son 1MB. Sin embargo, vale la pena por 'uniq -c' que es nuevo para mí.
notpeter