我该如何在Perl中递归读取目录?

13

我希望递归读取一个目录,在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;
}

我该如何解决这个问题?

6个回答

20

这样应该就可以了

 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');

1
所以,实际上 glob 不支持递归列出文件,对吧? - Baiyan Huang
没错,它只会从给定目录获取文件。 - Htbaa

9
你应该始终使用strict和warnings来帮助你调试代码。例如,Perl会警告你@files未声明。但是你函数的真正问题在于每次递归调用list_dirs时都会声明一个词法变量@paths,并且在递归步骤后没有将返回值推回去。
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;
}

使用这个程序,我只能得到一个结果,那就是我给程序的起始目录。 - Przemek
你能展示一下你用来检查这个的代码吗?在我的电脑上它正常工作。也许可以用"use Dumper; print Dumper list_dirs '/home'" 进行测试。 - mdom
请问您能解释一下find命令中发生了什么吗?对于Perl新手来说,它看起来很神秘。 - mabalenk

5
mdom的回答解释了你最初的尝试为何失败。我还建议您考虑更友好的File::Find替代方案。CPAN有几个选择,这是其中之一。
use strict;
use warnings;
use File::Find::Rule;
my @paths = File::Find::Rule->in(@ARGV);

还可以看这里:

  • SO答案提供了File::Find的CPAN替代方案。

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

3
我认为您的代码中以下行存在问题。
for my $eachFile (glob($path.'*'))

将变量 $path 改为 $rootpath。

这样就可以正确存储路径了。


1
我使用这个脚本来从我的USB闪存驱动器中删除由Mac OS X创建的隐藏文件,我通常在车里用它来听音乐,任何以".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" ;
  }
}

1
你可以使用这个方法作为递归文件搜索,以分离特定文件类型。
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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接