用于扫描目录树的Perl递归代码

用于扫描目录树的Perl递归代码,perl,recursion,directory,Perl,Recursion,Directory,在这个递归扫描目录的脚本中,我想知道当调用“scanddirectory($name)”时会发生什么->之后是否立即执行“next” 因为如果在每个循环之后@names被新目录填充,那么我们进入@names中的第一个目录,如果有其他目录,则会再次调用Scandirectory,但先前@names中的其他目录会被替换,因此它们不会被循环处理?对不起,如果我说不通的话 我知道已经有了一个用于此目的的模块,但我想提高对循环代码工作原理的理解,以便在其他情况下处理递归代码 sub ScanDirecto

在这个递归扫描目录的脚本中,我想知道当调用“scanddirectory($name)”时会发生什么->之后是否立即执行“next”

因为如果在每个循环之后@names被新目录填充,那么我们进入@names中的第一个目录,如果有其他目录,则会再次调用Scandirectory,但先前@names中的其他目录会被替换,因此它们不会被循环处理?对不起,如果我说不通的话

我知道已经有了一个用于此目的的模块,但我想提高对循环代码工作原理的理解,以便在其他情况下处理递归代码

sub ScanDirectory {

  my $workdir = shift;
  my $startdir = cwd;

  chdir $workdir or die;
  opendir my $DIR, '.' or die;
  my @names = readdir $DIR or die;
  closedir $DIR;

  foreach my $name (@names) {
    next if ($name eq ".");
    next if ($name eq "..");

    if (-d $name) {
      ScanDirectory($name);
      next;
    }

  }
  chdir $startdir or die;
}

ScanDirectory('.');
这是你的密码吗

在您调用的子例程中,定义了一个新的词汇范围变量,因此每个递归将创建该变量的一个新实例。如果您使用
我们的
而不是
我的
,它可能会起作用。用
我们的
定义的变量是打包范围,这意味着每个调用将使用相同的
@名称
变量。事实上,即使在那时也没有。您正在使用
readdir
清除变量的上一个值

你最好用它<代码>文件::查找随大多数Perl安装一起提供,因此它始终可用

use strict;
use warnings; 

use File::Find;

my @names;
find ( sub {
          next if $_ eq "." or $_ eq "..";
          push @names, $File::Find::name;
     }, "."
);
这更容易理解,更容易编写,更灵活,更高效,因为它不会递归地调用自己。大多数情况下,在函数中没有嵌入
sub
的情况下,您会看到这一点:

my @names;
find ( \&wanted, ".");

sub wanted {
    next if $_ eq "." or $_ eq "..";
    push @names, $File::Find::name;
}
如果子程序相当小,我更喜欢嵌入子程序。它防止子例程偏离
find
调用,并防止在子例程中使用没有明确定义的
@names
的神秘实例

好吧,他们都一样。这两个都是子例程引用(一个称为
通缉
,另一个是匿名子例程)。然而,
@names
的第一次使用似乎并不神秘,因为它是在
find
调用正上方的行中定义的

如果您必须从头开始编写自己的例程(可能是家庭作业?),那么不要使用递归。使用
push
反向
readdir
推入数组


然后,一次弹出一个数组项。如果您找到一个目录,请读取它(再次反向)并将其推送到阵列上。小心使用

这是一段写得很奇怪的代码,尤其是如果它是在一本书中发表的

您的混淆是因为
@names
数组是按词汇声明的,这意味着它只存在于当前块的范围内,并且对于特定堆栈帧(子例程调用)是唯一的。因此,
scan_directory
(本地标识符不应该真正包含大写字母)的每个调用都有自己独立的
@names
数组,当子例程退出时该数组消失,并且不存在“替换”内容的问题

此外,您所指的
next
是多余的:它会跳到
@names
数组的下一次迭代,如果没有它,这就是可能发生的情况

这样写会更好

sub scan_directory {
  my ($workdir) = @_;

  my $startdir = cwd;
  chdir $workdir or die $!;

  opendir my $dh, '.' or die $!;

  while (my $name = readdir $dh) {
    next if $name eq '.' or $name eq '..';
    scan_directory($name) if -d $name;
  }

  chdir $startdir or die $!;
}

scan_directory('.');

谢谢,我知道已经有了一个用于此目的的模块,但是我想提高我对循环代码工作原理的理解,这样我就可以在其他情况下处理递归代码。我建议检查一下--它是免费的,涵盖了很多使用递归的情况(包括目录遍历函数),以及在Perl中使用函数式编程技术的其他方法。您好,谢谢您的建议。这段代码实际上来自《perl系统管理》一书。在下一页中,作者将介绍file::find模块,以简化此任务。我只是想在继续之前完全理解这一点。