使用Perl在unix上获取目录和子目录大小的最快方法是什么?

问题描述 投票:3回答:7

我使用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。

perl filesystems
7个回答
2
投票

我曾遇到过类似的问题,并使用并行化方法来加快速度。由于您有大约20个顶级目录,因此这可能是一种非常简单的方法供您尝试。将顶层目录拆分为几个组(最好是多少组是经验问题),调用fork()几次并分析子进程中的目录大小。在子进程结束时,将结果写入一些临时文件。当所有孩子都完成后,从文件中读取结果并处理它们。


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会足够可靠。


1
投票

无论什么时候你想要加快某些事情,你的第一个任务就是找出什么是缓慢的。使用Devel::NYTProf等分析器分析程序,找出应该集中精力的地方。

除了重用上一个stat中的数据之外,我还要摆脱递归,因为Perl对它很可怕。我构建一个堆栈(或队列)并继续处理它,直到没有任何东西可以处理。


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;
}

1
投票

我看到了几个问题。一个@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);
}

1
投票

大回答很好。我稍微修改了它,因为我想在我的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 ...

0
投票

如果您的主目录是目录和文件inode的绝大多数消费者,那么不要计算它。计算系统的另一半并从中推导出系统其余部分的大小。 (你可以在几秒内从df获得用过的磁盘空间')。您可能需要添加一个小的“软糖”因子来获得相同的数字。 (还要记住,如果你计算一些可用空间为root,那么你将比Linux上的其他用户5%的ext2 / ext3有更多的额外用户,不了解HPUX)。

© www.soinside.com 2019 - 2024. All rights reserved.