递归Perl细节需要帮助

时间:2010-12-27 16:52:07

标签: perl file recursion directory

我认为这是一个简单的问题,但我已经坚持了一段时间了!我需要一双新鲜的眼睛。

问题是我在perl中有这个代码:

#!c:/Perl/bin/perl
use CGI qw/param/;  
use URI::Escape; 

print "Content-type: text/html\n\n";

my $directory = param ('directory'); 
$directory = uri_unescape ($directory); 

my @contents;
readDir($directory);


foreach (@contents) {
  print "$_\n";
}

#------------------------------------------------------------------------
sub readDir(){
 my $dir = shift;
    opendir(DIR, $dir) or die $!;
    while (my $file = readdir(DIR)) {
  next if ($file =~ m/^\./);
  if(-d $dir.$file)
  {
   #print $dir.$file. " ----- DIR\n";
   readDir($dir.$file);
  }
  push @contents, ($dir . $file);
    }
    closedir(DIR);
}

我试图让它递归。我需要拥有所有目录和子目录的所有文件,以及完整路径,以便将来可以打开文件。

但是我的输出只返回当前目录中的文件以及它找到的第一个目录中的文件。如果我在目录中有3个文件夹,则只显示第一个文件夹。

实施例。 cmd电话:

"perl readDir.pl directory=C:/PerlTest/"

由于

3 个答案:

答案 0 :(得分:1)

避免轮胎改造,使用CPAN。

use Path::Class::Iterator;
my $it = Path::Class::Iterator->new(
        root => $dir,
        breadth_first => 0
);
until ($it->done) {
        my $f = $it->next;
        push @contents, $f;
}

请确保您不要让人们将$dir设置为让他们看起来不希望他们看的地方。

答案 1 :(得分:1)

您的问题是目录句柄DIR的范围。 DIR具有全局范围,因此每次递归调用readDir都使用相同的DIR;因此,当您closdir(DIR)并返回调用者时,调用者在已关闭的目录句柄上执行readdir,一切都会停止。解决方案是使用本地目录句柄:

sub readDir {
    my ($dir) = @_;
    opendir(my $dh, $dir) or die $!;
    while(my $file = readdir($dh)) {
        next if($file eq '.' || $file eq '..');
        my $path = $dir . '/' . $file;
        if(-d $path) {
            readDir($path);
        }
        push(@contents, $path);
    }
    closedir($dh);
}

另请注意,如果(a)在每次递归调用时都不在$directory或(b)的末尾,则会丢失目录分隔符。 AFAIK,斜杠将在Windows内部转换为反斜杠,但你可能想要使用CPAN的路径修改模块(我只关心Unix系统,所以我没有任何建议)。

我还建议您将@contents的引用传递给readDir,而不是将其作为全局变量,减少错误,减少混淆。并且不要在sub定义上使用括号,除非您确切知道它们的作用以及它们的用途。对$directory进行一些健全检查和擦洗也是一个好主意。

答案 2 :(得分:0)

有许多模块可用于递归列出目录中的文件。

我最喜欢的是File :: Find :: Rule

    use strict ;
    use Data::Dumper ;
    use File::Find::Rule ;
    my $dir = shift ;   # get directory from command line

    my @files = File::Find::Rule->in( $dir );
    print Dumper( \@files ) ;

将文件列表发送到数组(程序正在执行)。

$VAR1 = [
      'testdir',
      'testdir/file1.txt',
      'testdir/file2.txt',
      'testdir/subdir',
      'testdir/subdir/file3.txt'
    ];

还有许多其他选项,例如只列出具有特定名称的文件。或者您可以将其设置为迭代器,How can I use File::Find

中对此进行了描述

How can I use File::Find in Perl?

如果您想坚持使用Perl Core附带的模块,请查看File :: Find。