在目录中按所有者对文件大小求和的最快方法

问题描述 投票:2回答:6

我正在使用以下命令使用别名来打印目录中所有者的所有文件大小的总和

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

但是,它似乎并不是一直很快。

是否有可能以其他方式获得相同的输出,但速度更快?

linux shell perl
6个回答
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相比,我期望良好的运行时间,当单个目录中的文件列表变长时,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; ,这里显而易见。 (这个分析应该重做,因为它已经有几年了。)


4
投票

另一个perl,显示按用户排序的总大小:

ls

2
投票

find解析输出 - 坏主意。

如何使用${dir}代替?

  • 从目录-maxdepth 1开始 限制到该目录级别(-type f) 限制文件(-printf "%u %s\n") 使用用户名和文件大小(以字节为单位)打印一行(-a
  • 通过perl过滤器运行结果 拆分每一行(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个文件。我必须思考为什么......


1
投票

不确定为什么在使用awk时标记perl的问题。

这是一个简单的perl版本:

glob
  • m//获取我们的文件列表
  • .丢弃..stat
  • %h的大小和uid
  • 积累在>>10的大小
  • 通过位移计算单位(qazxsw poi是整数除以1024)
  • 将uid映射到用户名(//提供后备)
  • 打印结果(未排序)
  • 注意:与其他一些答案不同,此代码不会递归到子目录中

要排除符号链接,子目录等,请将if更改为适当的-X测试。 (例如,(-f $_)(!-d $_ and !-l $_)等)。有关缓存统计结果的perl docs文件句柄优化,请参阅_


1
投票

我在操作系统中看到了一些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

0
投票

使用datamash(和Stefan Becker's find code):

find ${dir} -maxdepth 1 -type f -printf "%u\t%s\n" | datamash -sg 1 sum 2
© www.soinside.com 2019 - 2024. All rights reserved.