【问题标题】:What's the best strategy to delete a very huge folder using Perl?使用 Perl 删除一个非常大的文件夹的最佳策略是什么?
【发布时间】:2011-02-03 19:22:43
【问题描述】:

我需要删除给定文件夹下的所有内容(文件和文件夹)。问题是该文件夹中有数百万个文件和文件夹。所以我不想一次性加载所有文件名

逻辑应该是这样的:

  • 迭代文件夹而不加载所有内容
  • 获取文件或文件夹
  • 删除它 (详细说明文件或文件夹“X”已被删除)
  • 转到下一个

我正在尝试这样的事情:

sub main(){
  my ($rc, $help, $debug, $root)   = ();
  $rc = GetOptions ( "HELP"           => \$help,
                     "DEBUG"          => \$debug,
                     "ROOT=s"         => \$root);

  die "Bad command line options\n$usage\n" unless ($rc);
  if ($help) { print $usage; exit (0); }

  if ($debug) {
      warn "\nProceeding to execution with following parameters: \n";
      warn "===============================================================\n";
      warn "ROOT = $root\n";

  } # write debug information to STDERR

  print "\n Starting to delete...\n";  

  die "usage: $0 dir ..\n" unless $root;
  *name = *File::Find::name;
  find \&verbose, @ARGV;

}

sub verbose {
    if (!-l && -d _) {
        print "rmdir $name\n";
    } else {
        print "unlink $name\n";
    }
}

main();

它工作正常,但是每当“find”读取巨大的文件夹时,应用程序就会卡住,我可以看到 Perl 的系统内存增加直到超时。为什么?它是否试图一次性加载所有文件?

感谢您的帮助。

【问题讨论】:

  • 问题只有当你把它变成问题时才是问题。为什么要写rmrd /q/s(取决于你的操作系统)?
  • 我不同意使用 rm 或 rd 一定更好。使用 perl 内置函数更便携。
  • 我需要逐个文件删除并详细说明,可以吗?我在 Windows S.O.和 rmdir 只是卡住了。我想详细说明这个过程。
  • rmdir 可能没有卡住,只是在删除数百万个文件。 “冗长”这个过程会使其花费更长的时间,而数百万行的输出真的对你有帮助吗?
  • 看我的帖子,你需要finddepth而不是find。在选项中指定no_chdir => 1 比使用*name = *File::Find::name; 更好。另外,您忘记在sub verbose 中跳过...

标签: perl file-handling


【解决方案1】:

File::Path 中的 remove_tree 函数可以可移植地详细删除目录层次结构,如果需要,保留顶级目录。

use strict;
use warnings;
use File::Path qw(remove_tree);

my $dir = '/tmp/dir';
remove_tree($dir, {verbose => 1, keep_root => 1});

5.10 之前,使用来自File::Pathrmtree 函数。如果您仍然想要顶级目录,您可以再次mkdir

use File::Path;

my $dir = '/tmp/dir';
rmtree($dir, 1);  # 1 means verbose
mkdir $dir;

【讨论】:

  • 感谢您的回复,但是每当“rmtree”函数读取hude 文件夹时,应用程序就会卡住,我可以看到我的Perl 应用程序的系统内存在增加。为什么?它是否试图一次性加载所有文件?知道如何避免这种情况吗?
  • 是的,显然是这样。它加载目录中的所有内容以递归删除它们。看起来没有理由不能迭代。见github.com/gitpan/File-Path/blob/master/Path.pm#L333
【解决方案2】:

perlfaq 指出 File::Find 完成了遍历目录的艰苦工作,但工作并不难(假设您的目录树没有命名管道、块设备等):

sub traverse_directory {
    my $dir = shift;
    opendir my $dh, $dir;
    while (my $file = readdir($dh)) {
        next if $file eq "." || $file eq "..";
        if (-d "$dir/$file") {
            &traverse_directory("$dir/$file");
        } elsif (-f "$dir/$file") {
            # $dir/$file is a regular file
            # Do something with it, for example:
            print "Removing $dir/$file\n";
            unlink "$dir/$file" or warn "unlink $dir/$file failed: $!\n";
        } else {
            warn "$dir/$file is not a directory or regular file. Ignoring ...\n";
        }
    }
    closedir $dh;
    # $dir might be empty at this point. If you want to delete it:
    if (rmdir $dir) {
        print "Removed $dir/\n";
    } else {
        warn "rmdir $dir failed: $!\n";
    }
}

用您自己的代码替换文件或(可能)空目录,并在您要处理的树的根目录上调用此函数一次。如果您以前没有遇到过opendir/closedirreaddir-d-f,请查找它们的含义。

【讨论】:

  • 谢谢,我会尽力告诉你的。
  • 我在第三行出现错误opendir my $dh, $dir;。通过替换以下内容解决了它:my $dh; opendir $dh, $dir; 其余代码工作正常。谢谢
【解决方案3】:

有什么问题:

`rm -rf $folder`; // ??

【讨论】:

  • 我想详细说明一下流程,可以吗?
  • rm 有一个 -v 选项,可以在类 unix 操作系统下执行您想要的操作,但正如您所说,您在 Windows 下这对您没有帮助。
  • @Adam - 有 DOS 端口(许多)Unix 命令。我打赌其中一个,可以做到这一点:)
  • 我几乎每天都使用 GnuWin32 实用程序。这是包含rm的包的链接:gnuwin32.sourceforge.net/packages/coreutils.htm
  • @DVK, @daotoad:当然有,这些都是很好的例子。我得到的是rm -rf 确实有一个-v 选项,但是在反引号中使用rm 不是一个可移植的解决方案,这似乎在他对这个答案的最初评论中逃脱了操作。
【解决方案4】:

可以使用File::Find系统地遍历目录,删除其下的文件和目录。

【讨论】:

  • @Sinan:OP不想删除父目录。
  • 我想详细说明所有过程。
  • @Sinan:无论出于何种原因,OP 希望在删除所有文件时打印它们。 @André:看看File::Find。它为每个文件调用一个任意子程序。如果要打印文件名,请打印文件名。
  • @André:当然,正如 Sinan 指出的那样,您可以调用系统的递归详细删除目录的所有内容。您实际上不必重新实现它。
【解决方案5】:

好的,我放弃并使用了 Perl 内置函数,但你应该使用我完全忘记的 File::Path::rmtree

#!/usr/bin/perl

use strict; use warnings;
use Cwd;
use File::Find;

my ($clean) = @ARGV;
die "specify directory to clean\n" unless defined $clean;

my $current_dir = getcwd;
chdir $clean
    or die "Cannot chdir to '$clean': $!\n";

finddepth(\&wanted => '.');

chdir $current_dir
    or die "Cannot chdir back to '$current_dir':$!\n";

sub wanted {
    return if /^[.][.]?\z/;
    warn "$File::Find::name\n";
    if ( -f ) {
        unlink or die "Cannot delete '$File::Find::name': $!\n";
    }
    elsif ( -d _ ) {
        rmdir or die "Cannot remove directory '$File::Find::name': $!\n";
    }
    return;
}

【讨论】:

  • 感谢您的回复,但是每当“查找”功能读取hude 文件夹时,应用程序就会卡住,我可以看到Perl 的系统内存增加直到超时。为什么?它是否试图一次性加载所有文件?有什么想法吗?
【解决方案6】:

下载unix tools for windows 然后你就可以做rm -rv 或者其他的了。

Perl 是用于多种用途的出色工具,但使用专用工具似乎更好。

【讨论】:

    【解决方案7】:

    这是一种廉价的“跨平台”方法:

    use Carp    qw<carp croak>;
    use English qw<$OS_NAME>;
    use File::Spec;  
    
    my %deltree_op = ( nix => 'rm -rf %s', win => 'rmdir /S %s' );
    
    my %group_for
        = ( ( map { $_ => 'nix' } qw<linux UNIX SunOS> )
          , ( map { $_ => 'win' } qw<MSWin32 WinNT>    )
          );
    
    my $group_name = $group_for{$OS_NAME};
    sub chop_tree { 
       my $full_path = shift;
       carp( "No directory $full_path exists! We're done." ) unless -e $full_path;
       croak( "No implementation for $OS_NAME!" ) unless $group_name;
       my $format = $deltree_op{$group_name};
       croak( "Could not find command format for group $group_name" ) unless $format;
       my $command = sprintf( $format, File::Spec->canonpath( $full_path ));
       qx{$command};
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      • 2019-01-30
      • 2010-09-06
      • 2014-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多