我必须将带有数百万个文件的20TB文件系统移动到ZFS文件系统。所以我想知道文件大小,以便选择一个好的块大小。
我目前的想法是对每个文件进行`stat --format =“%s”,然后将文件分成多个文件夹。
#!/bin/bash
A=0 # nr of files <= 2^10
B=0 # nr of files <= 2^11
C=0 # nr of files <= 2^12
D=0 # nr of files <= 2^13
E=0 # nr of files <= 2^14
F=0 # nr of files <= 2^15
G=0 # nr of files <= 2^16
H=0 # nr of files <= 2^17
I=0 # nr of files > 2^17
for f in $(find /bin -type f); do
SIZE=$(stat --format="%s" $f)
if [ $SIZE -le 1024 ]; then
let $A++
elif [ $SIZE -le 2048 ]; then
let $B++
elif [ $SIZE -le 4096 ]; then
let $C++
fi
done
echo $A
echo $B
echo $C
这个脚本的问题是我无法让find
在for循环中工作。
问题
如何修复我的脚本?
有没有更好的方法来获取文件系统的所有文件大小?
答案 0 :(得分:2)
主要问题是您使用命令替换将find
的输出提供给for
循环。命令替换的工作原理是在括号(或反引号)中运行命令以完成,收集其输出,并将其替换为脚本。这不支持流式传输,这意味着for循环在find
扫描完全完成之前不会运行,并且你需要大量内存来缓冲find
的输出。
特别是因为您正在扫描多个TB级的文件,您将需要使用支持流式传输的内容,例如while
循环:
find /bin -type f | while read f; do
...
done
对于可以流式传输的内容,您的脚本至少可以正常工作,但请记住,此技术会强制您为找到的每个文件调用一次外部命令(stat
)。这将导致stat
命令的大量进程创建,销毁和启动成本。如果你有GNU find,那么在find
命令中输出每个文件大小的东西,例如-printf
选项,会表现得更好。
除此之外:循环体中的let
语句看起来不对。您正在扩展$A
,$B
和$C
变量的内容,而不是引用它们。你不应该在这里使用$
。
答案 1 :(得分:1)
如果只想查找100M到1000M之间的文件数,可以执行以下操作
find . -size +100M -size -1000M -type f | wc -l
答案 2 :(得分:0)
我会调查使用dd来读取zfs元数据,它应该包含在数据磁盘本身中。
这可能是一个糟糕的建议,可能会导致你浪费时间。但是使用bash爬行文件系统需要很长时间并且会损坏系统cpu利用率。
答案 3 :(得分:0)
find /bin/ -type f -printf "%s\n" > /tmp/a
然后将以下内容用作script.pl < /tmp/a
。
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my %h = ();
while (<STDIN>) {
chomp;
if ($_ <= 2**10) { $h{1} += 1}
elsif ($_ <= 2**11) { $h{2} += 1}
elsif ($_ <= 2**12) { $h{4} += 1}
elsif ($_ <= 2**13) { $h{8} += 1}
elsif ($_ <= 2**14) { $h{16} += 1}
elsif ($_ <= 2**15) { $h{32} += 1}
elsif ($_ <= 2**16) { $h{64} += 1}
elsif ($_ <= 2**17) { $h{128} += 1}
elsif ($_ > 2**17) { $h{big} += 1}
}
print Dumper \%h;
答案 4 :(得分:0)
尊敬的du command会更直接地为您提供尺码。