我正在使用以下命令使用别名来打印目录中所有者的所有文件大小的总和
ls -l $dir | awk ' NF>3 { file[$3]+=$5 } \
END { for( i in file) { ss=file[i]; \
if(ss >=1024*1024*1024 ) {size=ss/1024/1024/1024; unit="G"} else \
if(ss>=1024*1024) {size=ss/1024/1024; unit="M"} else {size=ss/1024; unit="K"}; \
format="%.2f%s"; res=sprintf(format,size,unit); \
printf "%-8s %12d\t%s\n",res,file[i],i }}' | sort -k2 -nr
但是,它似乎并不是一直很快。
是否有可能以其他方式获得相同的输出,但速度更快?
获取列表,添加大小,并按所有者排序(使用Perl)
perl -wE'
chdir (shift // ".");
for (glob ".* *") {
next if not -f;
($owner_id, $size) = (stat)[4,7]
or do { warn "Trouble stat for: $_"; next };
$rept{$owner_id} += $size
}
say (getpwuid($_)//$_, " => $rept{$_} bytes") for sort keys %rept
'
我没有对它进行基准测试,并且值得尝试反对目录迭代的方法,而不是glob
-ed(而我在glob
中发现related problem快得多)。
与ls
相比,我期望良好的运行时间,当单个目录中的文件列表变长时,File::Find会显着减慢。这是由于系统因此Perl也会受到影响,但据我记得它处理得更好。但是,我看到只有一次条目达到50万左右,而不是几千条,才会出现急剧减速,所以我不确定为什么它在你的系统上运行缓慢。
如果这需要递归,那么使用perl -MFile::Find -wE'
$dir = shift // ".";
find( sub {
return if not -f;
($owner_id, $size) = (stat)[4,7]
or do { warn "Trouble stat for: $_"; return };
$rept{$owner_id} += $size
}, $dir );
say (getpwuid($_)//$_, "$_ => $rept{$_} bytes") for keys %rept
'
。例如
du -sh
这将在2秒多一点的时间内扫描一个目录,该目录使用2.4 Gb(主要是子目录层次结构上的小文件)。 use warnings;
use strict;
use feature 'say';
use File::Find;
use Getopt::Long;
my %rept;
sub get_sizes {
return if not -f;
my ($owner_id, $size) = (stat)[4,7]
or do { warn "Trouble stat for: $_"; return };
$rept{$owner_id} += $size
}
my ($dir, $recurse) = ('.', '');
GetOptions('recursive|r!' => \$recurse, 'directory|d=s' => \$dir)
or die "Usage: $0 [--recursive] [--directory dirname]\n";
($recurse)
? find( { wanted => \&get_sizes }, $dir )
: find( { wanted => \&get_sizes,
preprocess => sub { return grep { -f } @_ } }, $dir );
say (getpwuid($_)//$_, " => $rept{$_} bytes") for keys %rept;
花了大约5秒钟(第一次)。
将这两者合并为一个脚本是合理的
File::Find::Rule
当非递归运行时(默认情况下),我发现这与上面的one-dir-only代码执行大致相同。
请注意,slower界面有许多便利,但在一些重要的用例中是#!/usr/bin/perl
use warnings;
use strict;
use autodie;
use feature qw/say/;
use File::Spec;
use Fcntl qw/:mode/;
my $dir = shift;
my %users;
opendir(my $d, $dir);
while (my $file = readdir $d) {
my $filename = File::Spec->catfile($dir, $file);
my ($mode, $uid, $size) = (stat $filename)[2, 4, 7];
$users{$uid} += $size if S_ISREG($mode);
}
closedir $d;
my @sizes = sort { $a->[0] cmp $b->[0] }
map { [ getpwuid($_) // $_, $users{$_} ] } keys %users;
local $, = "\t";
say @$_ for @sizes;
,这里显而易见。 (这个分析应该重做,因为它已经有几年了。)
另一个perl,显示按用户排序的总大小:
ls
从find
解析输出 - 坏主意。
如何使用${dir}
代替?
-maxdepth 1
开始
限制到该目录级别(-type f
)
限制文件(-printf "%u %s\n"
)
使用用户名和文件大小(以字节为单位)打印一行(-a
)END {...}
)
添加到键(字段0)下的哈希大小(字段1)
最后($ find ${dir} -maxdepth 1 -type f -printf "%u %s\n" | \
perl -ane '$s{$F[0]} += $F[1]; END { print "$_ $s{$_}\n" foreach (sort keys %s); }'
stefanb 263305714
)打印出哈希内容,按键排序,即用户名#!/usr/bin/perl
use strict;
use warnings;
use autodie;
use File::Spec;
my %users;
foreach my $dir (@ARGV) {
opendir(my $dh, $dir);
# files in this directory
while (my $entry = readdir($dh)) {
my $file = File::Spec->catfile($dir, $entry);
# only files
if (-f $file) {
my($uid, $size) = (stat($file))[4, 7];
$users{$uid} += $size
}
}
closedir($dh);
}
print "$_ $users{$_}\n" foreach (sort keys %users);
exit 0;
使用Perl的解决方案:
$ perl dummy.pl .
1000 263618544
测试运行:
find
有趣的区别。 Perl解决方案在我的测试目录中发现了比#!/usr/bin/perl
chdir($ARGV[0]) or die("Usage: $0 dir\n");
map {
if ( ! m/^[.][.]?$/o ) {
($s,$u) = (stat)[7,4];
$h{$u} += $s;
}
} glob ".* *";
map {
$s = $h{$_};
$u = !( $s >>10) ? ""
: !(($s>>=10)>>10) ? "k"
: !(($s>>=10)>>10) ? "M"
: !(($s>>=10)>>10) ? "G"
: ($s>>=10) ? "T"
: undef
;
printf "%-8s %12d\t%s\n", $s.$u, $h{$_}, getpwuid($_)//$_;
} keys %h;
解决方案多3个文件。我必须思考为什么......
不确定为什么在使用awk时标记perl的问题。
这是一个简单的perl版本:
glob
m//
获取我们的文件列表.
丢弃..
和stat
%h
的大小和uid>>10
的大小//
提供后备)要排除符号链接,子目录等,请将if
更改为适当的-X
测试。 (例如,(-f $_)
,(!-d $_ and !-l $_)
等)。有关缓存统计结果的perl docs文件句柄优化,请参阅_
。
我在操作系统中看到了一些awk吗?这是使用filefuncs扩展的GNU awk中的一个:
$ cat bar.awk
@load "filefuncs"
BEGIN {
FS=":" # passwd field sep
passwd="/etc/passwd" # get usernames from passwd
while ((getline < passwd)>0)
users[$3]=$1
close(passwd) # close passwd
if(path="") # set path with -v path=...
path="." # default path is cwd
pathlist[1]=path # path from the command line
# you could have several paths
fts(pathlist,FTS_PHYSICAL,filedata) # dont mind links (vs. FTS_LOGICAL)
for(p in filedata) # p for paths
for(f in filedata[p]) # f for files
if(filedata[p][f]["stat"]["type"]=="file") # mind files only
size[filedata[p][f]["stat"]["uid"]]+=filedata[p][f]["stat"]["size"]
for(i in size)
print (users[i]?users[i]:i),size[i] # print username if found else uid
exit
}
样本输出:
$ ls -l
total 3623
drwxr-xr-x 2 james james 3690496 Mar 21 21:32 100kfiles/
-rw-r--r-- 1 root root 4 Mar 21 18:52 bar
-rw-r--r-- 1 james james 424 Mar 21 21:33 bar.awk
-rw-r--r-- 1 james james 546 Mar 21 21:19 bar.awk~
-rw-r--r-- 1 james james 315 Mar 21 19:14 foo.awk
-rw-r--r-- 1 james james 125 Mar 21 18:53 foo.awk~
$ awk -v path=. -f bar.awk
root 4
james 1410
另一个:
$ time awk -v path=100kfiles -f bar.awk
root 4
james 342439926
real 0m1.289s
user 0m0.852s
sys 0m0.440s
还有一百万个空文件的另一个测试:
$ time awk -v path=../million_files -f bar.awk
real 0m5.057s
user 0m4.000s
sys 0m1.056s
使用datamash
(和Stefan Becker's find
code):
find ${dir} -maxdepth 1 -type f -printf "%u\t%s\n" | datamash -sg 1 sum 2