从命令提示符生成文件大小的分布


16

我有一个包含数百万个文件的文件系统,我希望在特定目录中递归查看文件大小的分布。我觉得用一些bash / awk fu完全可以做到,但是可以用一只手。基本上,我想要以下内容:

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

我觉得给定一个循环和一些有条件的log2文件大小的foo应该不算太糟,但是我似乎不太能到达那里。

相关问题:如何查找比x字节大/小的文件?

Answers:


22

这似乎工作得很好:

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
其中,左边的数字是该值到该值的两倍范围的下限,右边的数字是该范围内的文件数。


我编辑了您的答案,以使用find而不是ls,以便它是递归的并且不进行任何目录计数。任何人都想破解左列的输出吗?
notpeter 2013年

但是最初的问题是关于“在特定目录中文件大小的分布”,因此将更ls改为并不可行find。我将其恢复原状。
garyjohn

@notpeter:抱歉,我不认识您为问题的作者。我更改了答案以使其进行递归搜索。但是,在我的系统上,使用xargs的速度明显快于-exec,因此我使用了该方法。
garyjohn

1
别担心。现在我们可以删除我们假装的评论,这始终是正确的答案。;)
notpeter 2013年

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我定义了一个最小文件大小,以将所有小于1kb的文件收集到一个地方。在第二个中awkhuman(x)定义了函数以创建人类可读的大小。这部分基于此处的答案之一:https : //unix.stackexchange.com/questions/44040/a-standard-tool-to-convert-a-byte-count-into-human-kib-mib-etc -like-du-ls1

示例输出如下所示:

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

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);:这将提取文件大小,并将每个匹配项保存到array中k

  • if(!k[2]){print "1K"}:如果k[2]未定义,则文件大小为<1K。由于我在想您不关心这么小的尺寸,因此该脚本将为1K所有尺寸小于等于1K的文件打印。

  • else{printf "%.0f%s\n",k[1],k[2]} :如果文件大于1K,则将文件大小四舍五入到最接近的整数,然后连同其修饰符(K,M或G)一起打印。

  • sort | uniq -c :计算每行打印的次数(文件大小)。

  • sort -hk 2:按照人类可读格式根据第二个字段排序。这样,7G被排序8M


我感谢您的解释,我认为这对尝试解决问题的人很有帮助。就是说,您的脚本对我不起作用有两个原因:1)我的GNU LS很旧,因此为'ls -lh'(字节不是K / M / G / T)和2)提供了不同的人类可读大小输出,因为桶太多了。文件大小在1K和1G之间,有2000个存储桶,其中一半为1KB,一半为1MB。对于我来说,“ uniq -c”值得一提。
notpeter 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.