Как я могу рекурсивно читать каталоги в Perl?

Я хочу заново прочитать каталог, чтобы напечатать структуру данных на HTML-странице с помощью Template:: Toolkit. Но я вишу, как сохранить пути и файлы в форме, которую можно легко прочитать.

Моя идея началась следующим образом

sub list_dirs{

     my ($rootPath) = @_;
     my (@paths);

     $rootPath .= '/' if($rootPath !~ /\/$/);

     for my $eachFile (glob($path.'*'))
     {

         if(-d $eachFile)
         {
              push (@paths, $eachFile);

              &list_dirs($eachFile);
          }
          else
          {
              push (@files, $eachFile);
          }
     }

 return @paths;
}

Как я могу решить эту проблему?

Ответ 1

Это должно сделать трюк

 use strict;
 use warnings;
 use File::Find qw(finddepth);
 my @files;
 finddepth(sub {
      return if($_ eq '.' || $_ eq '..');
      push @files, $File::Find::name;
 }, '/my/dir/to/search');

Ответ 2

Вы всегда должны использовать строгие и предупреждения, чтобы помочь вам отладить ваш код. Например, Perl предупредил бы вас, что @files не объявлен. Но реальная проблема с вашей функцией заключается в том, что вы объявляете лексическую переменную @paths для каждого рекурсивного вызова list_dirs и не возвращаете возвращаемое значение после этапа рекурсии.

push @paths, list_dir($eachFile)

Если вы не хотите устанавливать дополнительные модули, возможно, вам поможет следующее решение:

use strict;
use warnings;
use File::Find qw(find);

sub list_dirs {
        my @dirs = @_;
        my @files;
        find({ wanted => sub { push @files, $_ } , no_chdir => 1 }, @dirs);
        return @files;
}

Ответ 3

Ответ mdom объясняет, как ваша первоначальная попытка сбилась с пути. Я также предлагаю вам рассмотреть более дружественные альтернативы File::Find. CPAN имеет несколько вариантов. Здесь один.

use strict;
use warnings;
use File::Find::Rule;
my @paths = File::Find::Rule->in(@ARGV);

Также см. здесь:

  • Ответ SO, предоставляющий CPAN альтернативы File::Find.

  • Вопрос SO в итераторах каталогов.

И вот переписывание вашего рекурсивного решения. Что нужно отметить: use strict; use warnings; и использование блока видимости для создания статической переменной для подпрограммы.

use strict;
use warnings;

print $_, "\n" for dir_listing(@ARGV);

{
    my @paths;
    sub dir_listing {
        my ($root) = @_;
        $root .= '/' unless $root =~ /\/$/;
        for my $f (glob "$root*"){
            push @paths, $f;
            dir_listing($f) if -d $f;
        }
        return @paths;
    }
}

Ответ 4

Я думаю, что у вас есть проблема в следующей строке вашего кода.

for my $eachFile (glob($path.'*'))

Вы изменяете переменную $path в $rootpath.

Он правильно сохранит путь.

Ответ 5

Я использую этот script для удаления скрытых файлов (созданных Mac OS X) с моего USB Pendrive, где я обычно использую его для прослушивания музыки в машине и любого файла, заканчивающегося ".mp3", даже если он начинается с "._", будет отображаться в списке автомобильных аудио.

#!/bin/perl

use strict;
use warnings;

use File::Find qw(find);

sub list_dirs {
        my @dirs = @_;
        my @files;
        find({ wanted => sub { push @files, $_ } , no_chdir => 1 }, @dirs);
        return @files;
}

if ( ! @ARGV || !$ARGV[0] ) {
  print "** Invalid dir!\n";
  exit ;
}


if ( $ARGV[0] !~ /\/Volumes\/\w/s ) {
  print "** Dir should be at /Volume/... > $ARGV[0]\n";
  exit ;
}

my @paths = list_dirs($ARGV[0]) ;

foreach my $file (@paths) {
  my ($filename) = ( $file =~ /([^\\\/]+)$/s ) ;

  if ($filename =~ /^\._/s ) {
    unlink $file ;
    print "rm> $file\n" ;
  }
}

Ответ 6

вы можете использовать этот метод в качестве рекурсивного поиска файлов, который разделяет определенные типы файлов,

my @files;
push @files, list_dir($outputDir);

sub list_dir {
        my @dirs = @_;
        my @files;
        find({ wanted => sub { push @files, glob "\"$_/*.txt\"" } , no_chdir => 1 }, @dirs);
        return @files;
}