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