我正在使用Perl stat()函数来获取目录及其子目录的大小。我有一个大约20个父目录的列表,其中有几千个递归子目录,每个子目录都有几百个记录。 脚本的主要计算部分如下所示:
sub getDirSize {
my $dirSize = 0;
my @dirContent = <*>;
my $sizeOfFilesInDir = 0;
foreach my $dirContent (@dirContent) {
if (-f $dirContent) {
my $size = (stat($dirContent))[7];
$dirSize += $size;
} elsif (-d $dirContent) {
$dirSize += getDirSize($dirContent);
}
}
return $dirSize;
}
脚本执行的时间超过一个小时,我想让它更快。
我尝试使用shell du
命令,但du
的输出(转换为字节)不准确。而且它也非常耗时。
我正在使用HP-UNIX 11i v1。
答案 0 :(得分:2)
我曾经遇到类似的问题,并使用并行化方法来加快速度。由于您有大约20个顶级目录,因此这可能是一种非常简单的方法供您尝试。
将顶层目录拆分为多个组(最好是多少组是经验问题),多次调用fork()
并分析子进程中的目录大小。在子进程结束时,将结果写入一些临时文件。当所有孩子都完成后,从文件中读取结果并处理它们。
答案 1 :(得分:2)
在sfink和samtregar的帮助下,在perlmonks上试试这个:
#!/usr/bin/perl
use warnings;
use strict;
use File::Find;
my $size = 0;
find( sub { $size += -f $_ ? -s _ : 0 }, shift(@ARGV) );
print $size, "\n";
这里我们正在递归指定目录的所有子目录,获取每个文件的大小,并通过使用特殊的'_'语法进行大小测试,重新使用文件测试中的stat。
我倾向于相信du会足够可靠。
答案 2 :(得分:1)
我看到了几个问题。一个@dirContent显式设置为&lt; *&gt;每次输入getDirSize时都会重置此项。结果将是一个无限循环,至少在你耗尽堆栈之前(因为它是一个递归调用)。其次,有一个特殊的文件句柄符号用于从stat调用中检索信息 - 下划线(_)。见:http://perldoc.perl.org/functions/stat.html。您的代码按原样调用stat三次,以获得基本相同的信息(-f,stat和-d)。由于文件I / O很昂贵,你真正想要的是调用一次stat然后使用“_”引用数据。以下是一些示例代码,我相信它可以完成您要执行的操作
#!/usr/bin/perl
my $size = 0;
getDirSize(".",\$size);
print "Size: $size\n";
sub getDirSize {
my $dir = shift;
my $size = shift;
opendir(D,"$dir");
foreach my $dirContent (grep(!/^\.\.?/,readdir(D))) {
stat("$dir/$dirContent");
if (-f _) {
$$size += -s _;
} elsif (-d _) {
getDirSize("$dir/$dirContent",$size);
}
}
closedir(D);
}
答案 3 :(得分:1)
下面是getDirSize()的另一个变体,它不需要引用保存当前大小的变量,并接受一个参数来指示是否应该考虑子目录:
#!/usr/bin/perl
print 'Size (without sub-directories): ' . getDirSize(".") . " bytes\n";
print 'Size (incl. sub-directories): ' . getDirSize(".", 1) . " bytes\n";
sub getDirSize
# Returns the size in bytes of the files in a given directory and eventually its sub-directories
# Parameters:
# $dirPath (string): the path to the directory to examine
# $subDirs (optional boolean): FALSE (or missing) = consider only the files in $dirPath, TRUE = include also sub-directories
# Returns:
# $size (int): the size of the directory's contents
{
my ($dirPath, $subDirs) = @_; # Get the parameters
my $size = 0;
opendir(my $DH, $dirPath);
foreach my $dirEntry (readdir($DH))
{
stat("${dirPath}/${dirEntry}"); # Stat once and then refer to "_"
if (-f _)
{
# This is a file
$size += -s _;
}
elsif (-d _)
{
# This is a sub-directory: add the size of its contents
$size += getDirSize("${dirPath}/${dirEntry}", 1) if ($subDirs && ($dirEntry ne '.') && ($dirEntry ne '..'));
}
}
closedir($DH);
return $size;
}
答案 4 :(得分:0)
每当你想要加快某些事情时,你的第一个任务就是找出什么是缓慢的。使用Devel::NYTProf等分析器来分析程序,找出应该集中精力的地方。
除了重用上一个stat中的数据之外,由于Perl很糟糕,我会摆脱递归。我将构建一个堆栈(或队列)并继续处理它,直到没有任何东西可以处理。
答案 5 :(得分:0)
如果您的主目录是目录和文件inode的绝大多数消费者,那么不要计算它。计算系统的另一半并从中推导出系统其余部分的大小。 (你可以在几毫秒内从df
获得用过的磁盘空间')。您可能需要添加一个小的“软糖”因子来获得相同的数字。 (还要记住,如果你将一些可用空间计算为root
,那么与Linux上的ext2 / ext3中的其他用户5%相比,你会有一些额外的空间,不知道HPUX)。
答案 6 :(得分:0)
大人们的回答是好的。我做了一些修改,因为我想获得Windows计算机上给定路径下所有文件夹的大小。
这就是我的方法。
#!/usr/bin/perl
use strict;
use warnings;
use File::stat;
my $dirname = "C:\\Users\\xxx\\Documents\\initial-docs";
opendir (my $DIR, $dirname) || die "Error while opening dir $dirname: $!\n";
my $dirCount = 0;
foreach my $dirFileName(sort readdir $DIR)
{
next if $dirFileName eq '.' or $dirFileName eq '..';
my $dirFullPath = "$dirname\\$dirFileName";
#only check if its a dir and skip files
if (-d $dirFullPath )
{
$dirCount++;
my $dirSize = getDirSize($dirFullPath, 1); #bytes
my $dirSizeKB = $dirSize/1000;
my $dirSizeMB = $dirSizeKB/1000;
my $dirSizeGB = $dirSizeMB/1000;
print("$dirCount - dir-name: $dirFileName - Size: $dirSizeMB (MB) ... \n");
}
}
print "folders in $dirname: $dirCount ...\n";
sub getDirSize
{
my ($dirPath, $subDirs) = @_; # Get the parameters
my $size = 0;
opendir(my $DH, $dirPath);
foreach my $dirEntry (readdir($DH))
{
stat("${dirPath}/${dirEntry}"); # Stat once and then refer to "_"
if (-f _)
{
# This is a file
$size += -s _;
}
elsif (-d _)
{
# This is a sub-directory: add the size of its contents
$size += getDirSize("${dirPath}/${dirEntry}", 1) if ($subDirs && ($dirEntry ne '.') && ($dirEntry ne '..'));
}
}
closedir($DH);
return $size;
}
1
;
输出:
1 - dir-name: acct-requests - Size: 0.458696 (MB) ...
2 - dir-name: environments - Size: 0.771527 (MB) ...
3 - dir-name: logins - Size: 0.317982 (MB) ...
folders in C:\Users\xxx\Documents\initial-docs: 3 ...