Создать распределение размеров файлов из командной строки

16

У меня есть файловая система, которая имеет пару миллионов файлов, и я хотел бы увидеть рекурсивное распределение размеров файлов в определенном каталоге. Я чувствую, что это вполне выполнимо с некоторыми bash / awk фу, но может использовать руку. В основном я хотел бы что-то вроде следующего:

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

Я чувствую, что это не должно быть слишком плохо, учитывая цикл и некоторый условный размер файла log2 foo, но я не могу этого достичь.

Связанный вопрос: Как я могу найти файлы, которые больше / меньше, чем x байтов? ,

notpeter
источник

Ответы:

21

Кажется, это работает довольно хорошо:

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

Его вывод выглядит так:

         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
где число слева - это нижний предел диапазона от этого значения до двойного значения, а число справа - количество файлов в этом диапазоне.

garyjohn
источник
Я отредактировал ваш ответ, чтобы использовать find вместо ls, чтобы он был рекурсивным и не выполнял никакого подсчета каталогов. Кто-нибудь хочет попробовать сделать вывод левой колонки?
Notpeter
Но первоначальный вопрос был о «распределении размеров файлов в определенном каталоге», поэтому не стоит менять на lsa find. Я возвращаю все обратно, как было.
garyjohn
@notpeter: Извините, я не узнал вас как автора вопроса. Я изменил свой ответ, чтобы сделать его поиск рекурсивным. В моей системе, хотя, используя xargsэто значительно быстрее , чем -exec, поэтому я использовал этот метод.
Гариджон
1
Не стоит беспокоиться. Теперь мы можем просто удалить наши комментарии, притворяясь, что это всегда был правильный ответ. ;)
Notpeter
14

Основываясь на ответе garyjohn, вот одна строка, которая также форматирует вывод в удобочитаемый для человека текст:

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

Вот расширенная версия этого:

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

В первом awkя определил минимальный размер файла, чтобы собрать все файлы размером менее 1 КБ в одном месте. Во втором awkфункция human(x)определена для создания удобочитаемого размера. Эта часть основана на одном из ответов здесь: /unix/44040/a-standard-tool-to-convert-a-byte-count-into-human-kib-mib-etc -как-дю-LS1

Пример вывода выглядит так:

  1k:    335
  2k:     16
 32k:      5
128k:     22
  1M:     54
  2M:     11
  4M:     13
  8M:      3
dzsuz87
источник
2

Попробуй это:

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 

ВЫХОД :

 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

ОБЪЯСНЕНИЕ:

  • find . -type f -exec ls -lh {} \;: достаточно просто, найдите файлы в текущем каталоге и запустите ls -lhна них

  • match($5,/([0-9.]+)([A-Z]+)/,k);: это извлечет размер файла и сохранит каждое совпадение в массив k.

  • if(!k[2]){print "1K"}: если k[2]не определено, размер файла <1K. Поскольку я полагаю, что вас не волнуют такие крошечные размеры, скрипт будет печатать 1Kдля всех файлов, размер которых <= 1К.

  • else{printf "%.0f%s\n",k[1],k[2]} : если файл больше 1 КБ, округлите размер файла до ближайшего целого числа и напечатайте вместе с его модификатором (K, M или G).

  • sort | uniq -c : подсчитать количество напечатанных строк (размер файла).

  • sort -hk 2: сортировка по второму полю в удобочитаемом формате. Таким образом, 7Gсортируется после 8M.

terdon
источник
Я ценю объяснения, я думаю, что это полезно для людей, пытающихся понять это. Тем не менее, ваш сценарий не работает для меня по двум причинам: 1) мой GNU LS устарел и поэтому выдает различный выводимый человеком размер для ls -lh (байты не K / M / G / T) и 2), потому что слишком много ведер При размере файла от 1 КБ до 1 ГБ имеется 2000 блоков, половина из которых составляет 1 КБ, половина - 1 МБ. Стоит, хотя для 'uniq -c', это для меня ново.
Notpeter