Générer une distribution des tailles de fichier à partir de l'invite de commande

16

J'ai un système de fichiers qui contient quelques millions de fichiers et j'aimerais voir une répartition des tailles de fichiers récursivement dans un répertoire particulier. J'ai l'impression que c'est totalement faisable avec un peu de bash / awk fu, mais je pourrais utiliser une main. En gros, j'aimerais quelque chose comme ceci:

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

Je pense que cela ne devrait pas être trop grave étant donné une boucle et une taille de fichier conditionnelle log2, mais je n'arrive pas à y arriver.

Question connexe: Comment puis-je trouver des fichiers plus gros / plus petits que x octets? .

notpeter
la source

Réponses:

21

Cela semble assez bien fonctionner:

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

Sa sortie ressemble à ceci:

         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
où le nombre à gauche est la limite inférieure d'une plage de cette valeur à deux fois cette valeur et le nombre à droite est le nombre de fichiers dans cette plage.

garyjohn
la source
J'ai modifié votre réponse pour utiliser find au lieu de ls afin qu'elle soit récursive et ne fasse aucun comptage de répertoire. Quelqu'un veut-il prendre une fissure pour améliorer la sortie de colonne de gauche?
notpeter
Mais la question initiale concernait la "distribution des tailles de fichiers dans un répertoire particulier", donc ce n'est pas OK de changer le lsen a find. Je remets ça comme ça.
garyjohn
@notpeter: Désolé, je ne vous ai pas reconnu comme l'auteur de la question. J'ai changé ma réponse pour la rendre récursive. Sur mon système, cependant, l'utilisation xargsest beaucoup plus rapide que -exec, j'ai donc utilisé cette méthode.
garyjohn
1
Pas de soucis. Maintenant, nous pouvons simplement supprimer nos commentaires qui prétendent que c'était toujours la bonne réponse. ;)
notpeter
14

Basé sur la réponse de garyjohn, voici un one-liner, qui formate également la sortie en lisible par l'homme:

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 voici la version étendue:

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

Dans le premier, awkj'ai défini une taille de fichier minimale pour collecter tous les fichiers de moins de 1 ko à un seul endroit. Dans le second awk, la fonction human(x)est définie pour créer une taille lisible par l'homme. Cette partie est basée sur l'une des réponses ici: /unix/44040/a-standard-tool-to-convert-a-byte-count-into-human-kib-mib-etc -like-du-ls1

L'exemple de sortie ressemble à ceci:

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

Essaye ça:

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 

PRODUCTION :

 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

EXPLICATION:

  • find . -type f -exec ls -lh {} \;: assez simple, trouvez les fichiers dans le répertoire courant et exécutez- ls -lhles

  • match($5,/([0-9.]+)([A-Z]+)/,k);: cela extraira la taille du fichier et enregistrera chaque correspondance dans le tableau k.

  • if(!k[2]){print "1K"}: si k[2]n'est pas défini, la taille du fichier est <1K. Puisque j'imagine que vous ne vous souciez pas de ces petites tailles, le script s'imprimera 1Kpour tous les fichiers dont la taille est <= 1K.

  • else{printf "%.0f%s\n",k[1],k[2]} : si le fichier est supérieur à 1 Ko, arrondissez la taille du fichier à l'entier le plus proche et imprimez avec son modificateur (K, M ou G).

  • sort | uniq -c : compte les occurrences de chaque ligne (taille de fichier) imprimée.

  • sort -hk 2: tri selon le deuxième champ au format lisible par l'homme. De cette façon, 7Gest trié après 8M.

terdon
la source
J'apprécie les explications, je pense que c'est utile pour les gens qui essaient de le comprendre. Cela dit, votre script ne fonctionne pas pour moi pour deux raisons 1) Mon GNU LS est ancien et donne donc une sortie de taille lisible humaine différente pour 'ls -lh' (octets pas K / M / G / T) et 2) parce que il y a trop de seaux. Avec des tailles de fichiers comprises entre 1K et 1G, il y a 2000 compartiments, dont la moitié sont de 1 Ko et la moitié de 1 Mo. Ça vaut le coup pour 'uniq -c' qui est nouveau pour moi.
notpeter