我使用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。
我曾遇到过类似的问题,并使用并行化方法来加快速度。由于您有大约20个顶级目录,因此这可能是一种非常简单的方法供您尝试。将顶层目录拆分为几个组(最好是多少组是经验问题),调用fork()
几次并分析子进程中的目录大小。在子进程结束时,将结果写入一些临时文件。当所有孩子都完成后,从文件中读取结果并处理它们。
在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会足够可靠。
无论什么时候你想要加快某些事情,你的第一个任务就是找出什么是缓慢的。使用Devel::NYTProf等分析器分析程序,找出应该集中精力的地方。
除了重用上一个stat中的数据之外,我还要摆脱递归,因为Perl对它很可怕。我构建一个堆栈(或队列)并继续处理它,直到没有任何东西可以处理。
下面是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;
}
我看到了几个问题。一个@dirContent显式设置为<*>,每次输入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);
}
大回答很好。我稍微修改了它,因为我想在我的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
;
OUTPUT:
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 ...
如果您的主目录是目录和文件inode的绝大多数消费者,那么不要计算它。计算系统的另一半并从中推导出系统其余部分的大小。 (你可以在几秒内从df
获得用过的磁盘空间')。您可能需要添加一个小的“软糖”因子来获得相同的数字。 (还要记住,如果你计算一些可用空间为root
,那么你将比Linux上的其他用户5%的ext2 / ext3有更多的额外用户,不了解HPUX)。