Genera distribuzione delle dimensioni dei file dal prompt dei comandi


16

Ho un filesystem che ha un paio di milioni di file e mi piacerebbe vedere una distribuzione di dimensioni dei file ricorsivamente in una particolare directory. Sento che questo è totalmente fattibile con un po 'di bash / awk fu, ma potrebbe usare una mano. Fondamentalmente mi piacerebbe qualcosa di simile al seguente:

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

Sento che questo non dovrebbe essere troppo male dato un loop e un po 'di file log2 condizionale condizionale, ma non riesco proprio ad arrivarci.

Domanda correlata: come posso trovare file più grandi / più piccoli di x byte? .

Risposte:


21

Questo sembra funzionare abbastanza bene:

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

Il suo output è simile al seguente:

         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
dove il numero a sinistra è il limite inferiore di un intervallo da quel valore al doppio di quel valore e il numero a destra è il numero di file in quell'intervallo.


Ho modificato la tua risposta per utilizzare find anziché ls in modo che fosse ricorsivo e non facesse alcun conteggio delle directory. Qualcuno vuole prendersi una breccia nel sollevare l'output della colonna di sinistra?
not

Ma la domanda originale era sulla "distribuzione delle dimensioni dei file in una directory particolare", quindi non è corretto cambiare lsin a find. Lo sto rimettendo com'era.
garyjohn,

@notpeter: mi dispiace, non ti ho riconosciuto come l'autore della domanda. Ho cambiato la mia risposta per farlo ricorsivamente. Sul mio sistema, tuttavia, l'utilizzo xargsè significativamente più veloce di -exec, quindi ho usato quel metodo.
garyjohn,

1
Nessun problema. Ora possiamo semplicemente eliminare i nostri commenti, facendo finta che sia sempre stata la risposta giusta. ;)
notre

14

Basato sulla risposta di garyjohn, ecco un one-liner, che formatta anche l'output in lettura umana:

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

Ecco la versione estesa di esso:

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

Nel primo awkho definito una dimensione minima del file per raccogliere tutti i file meno di 1kb in un unico posto. Nel secondo awk, la funzione human(x)è definita per creare una dimensione leggibile dall'uomo. Questa parte si basa su una delle risposte qui: /unix/44040/a-standard-tool-to-convert-a-byte-count-into-human-kib-mib-etc -come-du-LS1

L'output di esempio è simile a:

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

2

Prova questo:

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 

PRODUZIONE :

 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

SPIEGAZIONE :

  • find . -type f -exec ls -lh {} \;: Abbastanza semplice, trovare i file nella directory corrente ed eseguire ls -lhsu di essi

  • match($5,/([0-9.]+)([A-Z]+)/,k);: questo estrarrà la dimensione del file e salverà ogni corrispondenza nella matrice k.

  • if(!k[2]){print "1K"}: se k[2]non definito la dimensione del file è <1K. Dal momento che sto immaginando che non ti interessi di dimensioni così piccole, lo script stamperà 1Kper tutti i file la cui dimensione è <= 1K.

  • else{printf "%.0f%s\n",k[1],k[2]} : se il file è più grande di 1K, arrotondare la dimensione del file all'intero più vicino e stampare insieme al suo modificatore (K, M o G).

  • sort | uniq -c : conta le occorrenze di ciascuna riga (dimensione del file) stampata.

  • sort -hk 2: ordina in base al secondo campo in formato leggibile dall'uomo. In questo modo, 7Gviene ordinato dopo 8M.


Apprezzo le spiegazioni, penso che sia utile per le persone che cercano di capirlo. Detto questo, il tuo script non funziona per me per due motivi 1) Il mio GNU LS è vecchio e quindi fornisce output di dimensioni leggibili dall'uomo diverso per 'ls -lh' (byte non K / M / G / T) e 2) perché ci sono troppi secchi. Con file di dimensioni comprese tra 1K e 1G ci sono 2000 bucket, metà dei quali 1 KB e metà 1 MB. Ne vale la pena però per 'uniq -c' che è nuovo per me.
not
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.