我正在使用以下命令,并使用别名来按目录中的所有者打印所有文件大小的总和
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
但是,似乎并非一直如此。
是否可以通过其他方式获得相同的输出,但速度更快?
答案 0 :(得分:4)
另一个perl,显示按用户排序的总大小:
#!/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;
答案 1 :(得分:2)
解析ls
的输出-不好的主意。
如何改用find
?
${dir}
开始
-maxdepth 1
)-type f
)-printf "%u %s\n"
)-a
)END {...}
)打印出哈希内容,并按键(即用户名)排序$ 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
使用Perl的解决方案:
#!/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 dummy.pl .
1000 263618544
有趣的差异。与find
解决方案相比,Perl解决方案在我的测试目录中发现了3个文件。我必须考虑为什么会这样...
答案 2 :(得分:2)
从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
相比,我期望良好的运行时,因为单个目录中的文件列表变长,会使运行速度大大降低。这是由于系统的缘故,所以Perl也将受到影响,但据我所知,它处理起来要好得多。
但是,我发现仅当条目达到半百万左右(而不是几千个)时,获取列表的速度才显着下降,所以我不确定为什么它在您的系统上运行缓慢。
如果需要递归,请使用File::Find。例如
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
'
这会在2秒多一点的时间内扫描一个2.4 Gb的目录,该目录中大部分子文件位于子目录的层次结构中。 du -sh
花费了大约5秒钟(第一轮)。
将这两个脚本合而为一是合理的
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;
当以非递归方式运行时(默认情况下),我发现它的执行效果与上面的仅一个目录的代码相同。
请注意,File::Find::Rule界面有很多便利,但是在某些重要的用例中却是slower,在这里显然很重要。 (该分析已经使用了几年,应该重新进行。)
答案 3 :(得分:1)
不确定使用awk时为什么要在问题上标记问题。
这是一个简单的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;
glob
获取我们的文件列表m//
丢弃.
和..
stat
的大小和uid %h
中累积大小>>10
是1024除以整数)//
提供后备功能)要排除符号链接,子目录等,请将if
更改为适当的-X
测试。 (例如(-f $_)
,(!-d $_ and !-l $_)
等)。请参见_
文件句柄优化中的perl docs,以缓存统计信息结果。
答案 4 :(得分:1)
我在操作中看到awk吗?这是GNU awk中使用filefuncs扩展名的一个:
$ 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
答案 5 :(得分:0)
使用datamash
(和Stefan Becker's find
code):
find ${dir} -maxdepth 1 -type f -printf "%u\t%s\n" | datamash -sg 1 sum 2