Какая лучшая стратегия для удаления очень огромной папки с помощью Perl?

Мне нужно удалить все содержимое (файлы и папки) в данной папке. Проблема в том, что в папке миллионы файлов и папок. Поэтому я не хочу загружать все имена файлов за один раз.

Логика должна быть такой:

  • перебрать папку без загрузки всего
  • получить файл или папку
  • удалите его (подробно, что файл или папка "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();

Он работает нормально, но всякий раз, когда «найти» читает огромную папку, приложение зависает, и я вижу, что системная память для Perl увеличивается до истечения времени ожидания. Почему? Он пытается загрузить все файлы за один раз?

Спасибо за вашу помощь.


person André Diniz    schedule 02.04.2010    source источник
comment
проблема является проблемой только в том случае, если вы сделаете ее проблемой. Почему вы хотите писать rm или rd /q/s (в зависимости от вашей ОС)?   -  person Sinan Ünür    schedule 02.04.2010
comment
Я не согласен с тем, что обязательно лучше использовать rm или rd. Использование встроенных функций Perl более переносимо.   -  person frankc    schedule 02.04.2010
comment
Мне нужно удалить файл за файлом и подробно описать это, возможно ли это? Я под Windows S.O. и rmdir просто застрял. Я хотел бы подробно описать процесс.   -  person André Diniz    schedule 02.04.2010
comment
rmdir, вероятно, не застрял, просто работает над удалением миллионов файлов. Подробное описание процесса сделает его еще более длительным, и действительно ли вам помогут миллионы строк вывода?   -  person Jeff B    schedule 02.04.2010
comment
Да, я знаю это. Хорошо, забудьте об удалении. По какой-то причине я хочу перечислить все файлы на экране.   -  person André Diniz    schedule 02.04.2010
comment
Смотрите мой пост, вам нужно finddepth, а не find. Указание no_chdir => 1 в опции лучше, чем *name = *File::Find::name;. Кроме того, вы забыли пропустить . и .. в sub verbose.   -  person Sinan Ünür    schedule 02.04.2010


Ответы (7)


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/closedir, readdir, -d и -f, если вы не встречались с ними раньше.

person mob    schedule 02.04.2010
comment
Спасибо, я постараюсь и дам вам знать. - person André Diniz; 02.04.2010
comment
У меня ошибка в 3-й строке opendir my $dh, $dir;. Решил это, заменив на следующее: my $dh; opendir $dh, $dir; Остальной код работает нормально. Спасибо - person Pit; 11.01.2011

Функция remove_tree из File::Path может переносимо и подробно удалить иерархию каталогов, сохранив верхний каталог, если это необходимо.

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

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

До версии 5.10 используйте функцию rmtree из File::Path. Если вам по-прежнему нужен верхний каталог, вы можете просто снова mkdir.

use File::Path;

my $dir = '/tmp/dir';
rmtree($dir, 1);  # 1 means verbose
mkdir $dir;
person toolic    schedule 02.04.2010
comment
Спасибо за ответ, но всякий раз, когда функция rmtree читает папку hude, приложение зависает, и я вижу, что системная память для моего приложения Perl просто увеличивается. Почему? Он пытается загрузить все файлы за один раз? Есть идеи, как этого избежать? - person André Diniz; 02.04.2010
comment
Да, судя по всему. Он загружает все вещи в каталоге, чтобы рекурсивно удалить их. Похоже, нет причин, по которым его нельзя было бы сделать итеративным. См. github.com/gitpan/File-Path/blob/master. /Путь.pm#L333 - person Schwern; 04.04.2010

Что случилось с:

`rm -rf $folder`; // ??
person Jeff B    schedule 02.04.2010
comment
Я хотел бы подробно описать процесс, можно? - person André Diniz; 02.04.2010
comment
rm имеет опцию -v, которая будет делать то, что вы хотите, в unix-подобных ОС, но, как вы сказали, вы находитесь в Windows, это вам не поможет. - person Adam Bellaire; 02.04.2010
comment
@Adam Адам - ​​есть DOS-порты (множество) команд Unix. Держу пари, один из них может это сделать :) - person DVK; 03.04.2010
comment
Я использую утилиты GnuWin32 почти каждый день. Вот ссылка на пакет, содержащий rm: gnuwin32.sourceforge.net/packages/coreutils.htm< /а> - person daotoad; 03.04.2010
comment
@DVK, @daotoad: Конечно, есть, и это отличные примеры. Я имел в виду, что у rm -rf действительно есть опция -v, но использование rm в обратных кавычках не является переносимым решением, которое, казалось, ускользнуло от операции в его первоначальном комментарии к этому ответу. - person Adam Bellaire; 03.04.2010

Вы можете использовать File::Find для систематического обхода каталога и удаления файлов и каталогов в нем.

person codaddict    schedule 02.04.2010
comment
@Sinan: OP не хочет удалять родительский каталог. - person codaddict; 02.04.2010
comment
Я хотел бы подробно описать весь процесс. - person André Diniz; 02.04.2010
comment
@Sinan: И по какой-то причине ОП хочет, чтобы все файлы печатались по мере их удаления. @André: Посмотри на File::Find. Он вызывает произвольную подпрограмму для каждого файла. Если вы хотите напечатать имя файла, напечатайте имя файла. - person Cascabel; 02.04.2010
comment
@André: Конечно, как указал Синан, вы, вероятно, можете вызвать рекурсивное подробное удаление вашей системы для всего содержимого каталога. Вам действительно не нужно повторно реализовывать его. - person Cascabel; 02.04.2010

Хорошо, я сдался и использовал встроенные функции 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;
}
person Sinan Ünür    schedule 02.04.2010
comment
Спасибо за ответ, но всякий раз, когда функция поиска читает папку hude, приложение зависает, и я вижу, что системная память для Perl увеличивается до истечения времени ожидания. Почему? Он пытается загрузить все файлы за один раз? Есть идеи? - person André Diniz; 02.04.2010

Загрузите инструменты Unix для Windows, а затем вы можете делать rm -rv или что-то еще.

Perl — отличный инструмент для многих целей, но с этим, кажется, лучше справиться специализированный инструмент.

person justintime    schedule 02.04.2010

Вот дешевый "кросс-платформенный" метод:

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};
}
person Axeman    schedule 02.04.2010