perl如何获取一个目录中所有文件的大小总和



QUOTE:
原帖由 smzml 于 2006-3-19 16:34 发表
谢谢5楼大哥。但是这样只能统计某个目录下的文件,如果有子目录和子文件,它数不出来呀。
而且,如果文件有几千个,统计效率好象很慢,有没有快一点的办法呀?

不管你用啥方法,其实都是递归遍历,只不过有些实现的效率比较高,有些比较低而已。
我前几天恰好写过一个类似的东西,http://www.icylife.net/yunshu/show.php?id=647。

[Copy to clipboard] [ - ]
CODE:
#!/usr/bin/perl
use strict;
use warnings;
use threads;
use threads::shared;
use Thread::Semaphore;

my ($size, $dircnt, $filecnt) = (0, 0, 0);
my %size_hash : shared;
my @sub_dirs;

if( @ARGV != 3 )
{
        print "usage: $0  <rootdir>   <depth>   <topn> \n";
        print "example:$0   c:\\    3   15\n";

        exit;
}
my $root = $ARGV[0];
my $sub = $ARGV[1];
my $topn = $ARGV[2];

if( $root !~ /\\$/ && $root !~ /\/$/ )
{
        $root = $root."\\";
}

print "开始搜寻$root下面的$sub级子目录.....\n";
&GetSubDirectory( );

print "开始获取目录大小......\n";
my $max_thread_count = 100;
my $semaphore = new Thread::Semaphore( $max_thread_count );
foreach my $tmp_dir( @sub_dirs )
{
        $semaphore->down( );
        my $thread = threads->create( \&GetDirectorySize, $tmp_dir );
        $thread->detach();
}

&Wait2Quit( );

my $index = 0;
foreach my $key ( sort { $size_hash{$b} <=> $size_hash{$a} } keys %size_hash)
{
        if( $index == $topn )
        {
                last;
        }
        printf( "%-50s%18s 字节\n", $key, $size_hash{$key} );
        $index ++;
}

sub Wait2Quit
{
        my $num = 0;
        while( $num < $max_thread_count )
        {
                # 尝试获取信号量,当能够获取到最大线程数个信号量时,表示所有线程都结束了
                $semaphore->down( );
                $num ++;
        }
        #print "All $max_thread_count thread quit\n";
}

sub GetSubDirectory
{
        my @dirs;
        push( @dirs, $root );

        my ( $current_dir, $file );
        while ( $current_dir = pop(@dirs) )
        {
                # 根据路径中\或者/的次数,确定当前是几级目录
                #print "processing $current_dir:";
                my $sub_count = 0;
                while( $current_dir=~/\\|\//g )
                {
                        $sub_count++;
                }
                #print " $sub_count\n";
                # 等于指定深度的,加入到最终数组中
                if( $sub_count == $sub )
                {
                        #print $current_dir."\n";
                        push( @sub_dirs, $current_dir );
                        next;
                }
                # 过深的目录不予处理
                elsif( $sub_count > $sub )
                {
                        next;
                }

                # 目录结尾加上\
                if( $current_dir !~ /\\$/ && $current_dir !~ /\/$/ )
                {
                        $current_dir = $current_dir."\\";
                }

                # 路径不足的打开目录,查找子目录
                local *DH;
                if ( !opendir(DH, $current_dir) )
                {
                        #print "Cannot opendir $current_dir: $!\n";
                        next;
                }

                # 读取文件和子目录列表
                foreach( readdir(DH) )
                {
                        # 当前目录和上级目录跳过
                        if ($_ eq "." || $_ eq "..")
                        {
                                next;
                        }
                        $file = $current_dir.$_;
                        if ( !-l $file && -d $file )
                        {
                                #print "Add $file\n";
                                push(@dirs, $file);
                        }
                        #&process($file);
                }
                closedir(DH);
        }
}

sub GetDirectorySize
{
        my $first_dir = shift;
        $size_hash{ $first_dir } = 0;

        my @dirs;
        push( @dirs, $first_dir );

        my ( $current_dir, $file );
        while ( $current_dir = pop(@dirs) )
        {
                # 目录结尾加上\
                if( $current_dir !~ /\\$/ && $current_dir !~ /\/$/ )
                {
                        $current_dir = $current_dir."\\";
                }

                # 打开目录,查找子目录以及文件
                local *DH;
                if ( !opendir(DH, $current_dir) )
                {
                        #print "Cannot opendir $current_dir: $!\n";
                        next;
                }

                # 读取文件和子目录列表
                foreach( readdir(DH) )
                {
                        # 当前目录和上级目录跳过
                        if ($_ eq "." || $_ eq "..")
                        {
                                next;
                        }
                       
                        # 形成绝对路径
                        $file = $current_dir.$_;

                        # 处理目录或符号链接
                        if ( !-l $file && -d $file )
                        {
                                push(@dirs, $file);
                        }
                       
                        $size_hash{ $first_dir } += -s $file;
                }
                closedir(DH);
        }

        $semaphore->up( );
}

你为啥不用 File::Find?


QUOTE:
原帖由 flw 于 2008-12-12 11:08 发表
你为啥不用 File::Find?

遍历目录用这个很方便
因为对比过,File::Find没有我自己写的速度快。


QUOTE:
原帖由 flw 于 2008-12-12 11:08 发表
你为啥不用 File::Find?

遍历目录的话,File_Find 模块的确不方便,正常情况下,用stat函数获得每个文件的大小,但是在累加求和的时候,发现每次都要去调用wanted子过程,不是多方便。