Gere distribuição de tamanhos de arquivo no prompt de comando

16

Eu tenho um sistema de arquivos que possui alguns milhões de arquivos e gostaria de ver uma distribuição de tamanhos de arquivos recursivamente em um diretório específico. Eu sinto que isso é totalmente factível com algum bash / awk fu, mas poderia usar uma mão. Basicamente, eu gostaria de algo como o seguinte:

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

Eu sinto que isso não deveria ser tão ruim, dado um loop e alguns arquivos de log2 condicionais foo, mas não consigo chegar lá.

Pergunta relacionada: Como posso encontrar arquivos maiores / menores que x bytes? .

notpeter
fonte

Respostas:

21

Isso parece funcionar muito bem:

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

Sua saída é assim:

         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
onde o número à esquerda é o limite inferior de um intervalo desse valor para o dobro desse valor e o número à direita é o número de arquivos nesse intervalo.

garyjohn
fonte
Editei sua resposta para usar find em vez de ls, para que fosse recursivo e não fizesse nenhuma contagem de diretório. Alguém quer dar uma brecha na limpeza da coluna da esquerda?
notpeter
Mas a pergunta original era sobre "distribuição de tamanhos de arquivo em um diretório específico", portanto, não há problema em mudar lspara a find. Estou colocando de volta do jeito que estava.
precisa saber é o seguinte
@notpeter: Desculpe, eu não o reconheci como o autor da pergunta. Alterei minha resposta para fazer a pesquisa recursivamente. No meu sistema, porém, o uso xargsé significativamente mais rápido do que -exec, então eu usei esse método.
garyjohn
1
Não se preocupe. Agora podemos simplesmente excluir nossos comentários, fingimos que sempre foi a resposta certa. ;)
notpeter 14/03
14

Com base na resposta de garyjohn, aqui está uma linha, que também formata a saída para legível por 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) }'

Aqui está a versão expandida:

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) }' 

Na primeira awk, defini um tamanho mínimo de arquivo para coletar todos os arquivos com menos de 1kb em um único local. No segundo awk, a função human(x)é definida para criar um tamanho legível por humanos. Esta parte é baseada em uma das respostas aqui: /unix/44040/a-standard-tool-to-convert-a-byte-count-into-human-kib-mib-etc -like-du-ls1

A saída de amostra é semelhante a:

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

Tente o seguinte:

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 

RESULTADO :

 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

EXPLICAÇÃO:

  • find . -type f -exec ls -lh {} \;: simples o suficiente, encontre arquivos no diretório atual e execute ls -lh-os

  • match($5,/([0-9.]+)([A-Z]+)/,k);: isso extrairá o tamanho do arquivo e salvará cada correspondência na matriz k.

  • if(!k[2]){print "1K"}: se k[2]for indefinido, o tamanho do arquivo é <1K. Como estou imaginando que você não se importa com tamanhos tão pequenos, o script será impresso 1Kpara todos os arquivos cujo tamanho é <= 1K.

  • else{printf "%.0f%s\n",k[1],k[2]} : se o arquivo for maior que 1K, arredonde o tamanho do arquivo para o número inteiro mais próximo e imprima junto com seu modificador (K, M ou G).

  • sort | uniq -c : conte as ocorrências de cada linha (tamanho do arquivo) impressa.

  • sort -hk 2: classifique de acordo com o segundo campo no formato legível por humanos. Dessa forma, 7Gé classificado depois 8M.

Terdon
fonte
Aprecio as explicações, acho útil para as pessoas que tentam descobrir. Dito isso, seu script não funciona para mim por dois motivos: 1) Meu GNU LS é antigo e, portanto, fornece saída de tamanho legível por humanos diferente para 'ls -lh' (bytes não K / M / G / T) e 2) porque há muitos baldes. Com tamanhos de arquivo entre 1K e 1G, existem 2000 buckets, metade dos quais 1KB metade dos quais 1MB. Vale a pena, porém, para 'uniq -c' que é novo para mim.
notpeter 13/03