Wygeneruj rozkład rozmiarów plików z wiersza polecenia

16

Mam system plików, który ma kilka milionów plików i chciałbym zobaczyć rozkład rozmiarów plików rekurencyjnie w określonym katalogu. Wydaje mi się, że jest to całkowicie wykonalne w przypadku niektórych bash / awk fu, ale przydałaby mi się ręka. Zasadniczo chciałbym coś takiego:

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

Wydaje mi się, że nie powinno być tak źle biorąc pod uwagę pętlę i foo warunkowego rozmiaru pliku log2, ale wydaje mi się, że nie mogę się tam dostać.

Powiązane pytanie: Jak znaleźć pliki większe / mniejsze niż x bajtów? .

notpeter
źródło

Odpowiedzi:

22

To wydaje się działać całkiem dobrze:

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

Jego dane wyjściowe wyglądają następująco:

         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
gdzie liczba po lewej to dolna granica zakresu od tej wartości do dwukrotności tej wartości, a liczba po prawej to liczba plików w tym zakresie.

garyjohn
źródło
Zredagowałem twoją odpowiedź, aby użyć find zamiast ls, aby była rekurencyjna i nie liczyła żadnego katalogu. Ktoś chce rzucić okiem na upiększanie wyjścia z lewej kolumny?
notpeter 13.03.2013
Ale pierwotne pytanie dotyczyło „dystrybucji rozmiarów plików w określonym katalogu”, więc zmiana lspliku na find. Przywracam to do poprzedniego stanu.
garyjohn
@notpeter: Przepraszam, nie rozpoznałem cię jako autora pytania. Zmieniłem odpowiedź, aby wyszukiwać rekurencyjnie. Jednak w moim systemie używanie xargsjest znacznie szybsze niż -exec, więc zastosowałem tę metodę.
garyjohn
1
Bez obaw. Teraz możemy po prostu usunąć nasze komentarze są udawane, że zawsze była to właściwa odpowiedź. ;)
notpeter
14

Oparty na odpowiedzi garyjohna, tutaj jest linijka, która również formatuje dane wyjściowe do postaci czytelnej dla człowieka:

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

Oto jego rozszerzona wersja:

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

W pierwszym awkzdefiniowałem minimalny rozmiar pliku, aby zebrać wszystkie pliki mniejsze niż 1kb w jednym miejscu. W drugim zdefiniowano awkfunkcję, human(x)aby utworzyć czytelny dla człowieka rozmiar. Ta część oparta jest na jednej z odpowiedzi tutaj: /unix/44040/a-standard-tool-to-convert-a-byte-count-into-human-kib-mib-etc -like-du-ls1

Przykładowy wynik wygląda następująco:

  1k:    335
  2k:     16
 32k:      5
128k:     22
  1M:     54
  2M:     11
  4M:     13
  8M:      3
dzsuz87
źródło
2

Spróbuj tego:

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 

WYNIK :

 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

OBJAŚNIENIE:

  • find . -type f -exec ls -lh {} \;: dość proste, znajdź pliki w bieżącym katalogu i uruchom ls -lhje na nich

  • match($5,/([0-9.]+)([A-Z]+)/,k);: spowoduje to wyodrębnienie rozmiaru pliku i zapisanie każdego dopasowania w tablicy k.

  • if(!k[2]){print "1K"}: jeśli nie k[2]jest zdefiniowany, rozmiar pliku wynosi <1 KB. Ponieważ wyobrażam sobie, że nie przejmujesz się tak małymi rozmiarami, skrypt zostanie wydrukowany 1Kdla wszystkich plików o rozmiarze <= 1 KB.

  • else{printf "%.0f%s\n",k[1],k[2]} : jeśli plik jest większy niż 1 KB, zaokrąglij rozmiar pliku do najbliższej liczby całkowitej i wydrukuj wraz z jego modyfikatorem (K, M lub G).

  • sort | uniq -c : policz wystąpienia każdej drukowanej linii (rozmiaru pliku).

  • sort -hk 2: sortuj według drugiego pola w formacie czytelnym dla człowieka. W ten sposób 7Gposortowano według 8M.

terdon
źródło
Doceniam wyjaśnienia, myślę, że jest to pomocne dla osób próbujących je zrozumieć. To powiedziawszy, twój skrypt nie działa dla mnie z dwóch powodów: 1) Mój GNU LS jest stary, a więc daje inny czytelny dla człowieka rozmiar wyjściowy dla 'ls -lh' (bajty nie K / M / G / T) i 2), ponieważ jest za dużo wiader. Przy rozmiarach plików od 1K do 1G istnieje 2000 segmentów, z których połowa to 1KB, z czego połowa to 1 MB. Warto jednak dla „uniq -c”, co jest dla mnie nowe.
nie