【问题标题】:Perl recursive code for scanning directory tree用于扫描目录树的 Perl 递归代码
【发布时间】:2014-09-30 01:33:53
【问题描述】:

在这个递归扫描目录的脚本中,我想知道当“ScanDirectory($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('.');

【问题讨论】:

  • 谢谢,我知道已经有一个用于此目的的模块,但我想加深对这个循环代码如何工作的理解,以便在其他情况下处理递归代码。
  • 我推荐查看Higher Order Perl——它是免费的,涵盖了很多使用递归(包括目录遍历函数)的情况,以及可以使用函数式编程技术的其他方式在 Perl 中。

标签: perl recursion directory


【解决方案1】:

这是你的代码吗?

在您调用my @names = readdir 的子例程中,它定义了一个新的词法范围变量,因此每次递归都会创建该变量的一个新实例。如果您使用our 而不是my,它可能会起作用。使用our 定义的变量是打包范围,这意味着每次调用都将使用相同的@names 变量。其实那时也没有。您正在使用 readdir 清除变量的先前值。

使用File::Find 会更好。 File::Find 随大多数 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的神秘实例在没有明确定义的情况下在子程序中使用。

好的,它们都是一样的。两者都是子例程引用(一个称为wanted,一个是匿名子例程)。然而,@names 的第一次使用似乎并不那么神秘,因为它实际上是在 find 调用正上方的行中定义的。

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

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

【讨论】:

  • 您好,感谢您的建议。该代码实际上来自一本书“使用 perl 进行系统管理”。在下一页中,作者介绍了 file::find 模块以简化此任务。我只是想在继续之前完全理解这一点。
【解决方案2】:

这是写得很奇怪的代码,尤其是出版在书中的代码。

您的困惑是因为@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('.');

【讨论】:

    猜你喜欢
    • 2012-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    • 1970-01-01
    • 2017-09-05
    相关资源
    最近更新 更多