Hasilkan distribusi ukuran file dari prompt perintah

16

Saya punya sistem file yang memiliki beberapa juta file dan saya ingin melihat distribusi ukuran file secara rekursif di direktori tertentu. Aku merasa ini benar-benar bisa dilakukan dengan beberapa bash / awk fu, tetapi bisa menggunakan tangan. Pada dasarnya saya ingin sesuatu seperti yang berikut:

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

Saya merasa seperti ini seharusnya tidak terlalu buruk diberikan loop dan beberapa file kondisional log2 bersyarat foo, tapi saya sepertinya tidak bisa sampai di sana.

Pertanyaan Terkait: Bagaimana saya bisa menemukan file yang lebih besar / lebih kecil dari x byte? .

notpeter
sumber

Jawaban:

21

Ini tampaknya bekerja dengan cukup baik:

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

Outputnya terlihat seperti ini:

         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
di mana angka di sebelah kiri adalah batas bawah rentang dari nilai itu menjadi dua kali lipat dari nilai itu dan angka di sebelah kanan adalah jumlah file dalam kisaran itu.

garyjohn
sumber
Saya mengedit jawaban Anda untuk menggunakan find bukan ls sehingga rekursif dan tidak melakukan penghitungan direktori. Adakah yang mau mencoba membuat kolom kolom kiri?
notpeter
Tetapi pertanyaan aslinya adalah tentang "distribusi ukuran file di direktori tertentu", jadi tidak apa-apa untuk mengubah lske a find. Aku meletakkannya kembali seperti semula.
garyjohn
@ notpeter: Maaf, saya tidak mengenali Anda sebagai penulis pertanyaan. Saya mengubah jawaban saya untuk membuatnya mencari secara rekursif. Namun pada sistem saya, menggunakan xargssecara signifikan lebih cepat daripada -exec, jadi saya menggunakan metode itu.
garyjohn
1
Jangan khawatir. Sekarang kita tinggal menghapus komentar kita yang berpura-pura itu selalu jawaban yang tepat. ;)
notpeter
14

Berdasarkan jawaban garyjohn, ini adalah one-liner, yang juga memformat output menjadi dapat dibaca manusia:

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

Ini adalah versi yang diperluas:

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

Pada bagian pertama awksaya mendefinisikan ukuran file minimum untuk mengumpulkan semua file kurang dari 1kb ke satu tempat. Dalam yang kedua awk, fungsi human(x)didefinisikan untuk membuat ukuran yang dapat dibaca manusia. Bagian ini didasarkan pada salah satu jawaban di sini: /unix/44040/a-standard-tool-to-convert-a-byte-count-into-human-kib-mib-etc -seperti du-ls1

Output sampel terlihat seperti:

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

Coba ini:

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 

OUTPUT:

 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

PENJELASAN:

  • find . -type f -exec ls -lh {} \;: cukup sederhana, cari file dalam direktori saat ini dan jalankan ls -lhdi dalamnya

  • match($5,/([0-9.]+)([A-Z]+)/,k);: ini akan mengekstraksi ukuran file, dan menyimpan setiap kecocokan ke dalam array k.

  • if(!k[2]){print "1K"}: jika k[2]tidak terdefinisi ukuran file adalah <1K. Karena saya membayangkan Anda tidak peduli dengan ukuran sekecil itu, skrip akan mencetak 1Kuntuk semua file yang ukurannya <= 1K.

  • else{printf "%.0f%s\n",k[1],k[2]} : jika file lebih besar dari 1K, bulatkan ukuran file ke integer terdekat dan cetak bersama dengan modifikasinya (K, M, atau G).

  • sort | uniq -c : hitung kemunculan setiap baris (ukuran file) yang dicetak.

  • sort -hk 2: urutkan berdasarkan bidang kedua dalam format yang dapat dibaca manusia. Dengan cara ini, 7Gdiurutkan setelahnya 8M.

terdon
sumber
Saya menghargai penjelasannya, saya pikir ini membantu bagi orang yang mencoba mencari tahu. Yang mengatakan, skrip Anda tidak berfungsi untuk saya karena dua alasan 1) GNU LS saya sudah tua dan memberikan keluaran ukuran manusia yang berbeda untuk 'ls -lh' (byte bukan K / M / G / T) dan 2) karena terlalu banyak ember. Dengan ukuran file antara 1K dan 1G ada 2000 ember, setengahnya adalah 1KB setengahnya adalah 1MB. Layak untuk 'uniq -c' yang baru bagi saya.
notpeter