【问题标题】:Perl script that recurses through a directory tree通过目录树递归的 Perl 脚本
【发布时间】:2013-02-12 05:18:55
【问题描述】:

我想编写一个 Perl 脚本,它从目录树的顶部开始(在命令行参数中提供)并递归地遍历每个子目录,对每个文件执行特定操作。

我为此使用finddepth,但是当我在距离基目录两级或更高级别的目录上运行脚本时,它似乎不起作用。

这是我的代码:

#!/usr/local/bin/perl -w

use strict;

use File::Copy;
use File::Find;
use File::Basename;
use File::Path;

finddepth(\&file_list, @ARGV);

sub file_list {

    my ($file_path, $name, $path, $suffix);

    $file_path = $File::Find::name;

    ($name, $path, $suffix) = fileparse($file_path, /\.*/);

    my $fullname = $name . $suffix;
    my $file = $fullname;

    if ($file =~ /^[^\.].*[^\.pl]$/) {

        copy($file, "$file.orig");

        open(FILE, "$file");
        my @file_data = <FILE>;
        close(FILE);

        open(FOUT, ">$file") or die " \n File cannot be opened !";

        foreach my $line (@file_data) {
            if ($line =~ /^\s+Error:/) {
                $line =~ s/([^-]\d+)/ \*\*/gc;
                print FOUT $line;
            }
            else {
                print FOUT $line;
            }
        }
        close(FOUT);
    }
}

始终抛出以下警告/错误:

  1. 读取关闭的文件句柄
  2. 无法打开文件!

我似乎无法弄清楚为什么会这样。我试图让我的问题尽可能具体。如果您需要更多信息,请告诉我。谢谢。

【问题讨论】:

  • 您使用的是strict,但为什么不使用warnings
  • 这些警告/错误是在哪里抛出的?他们不显示文件名吗?行号?
  • 你的正则表达式/^[^\.].*[^\.pl]$/应该匹配什么?
  • 这是在第 37 行引发的在关闭的文件句柄 上读取和无法打开文件时引发的警告。此外,我从代码中删除了警告,只是为了看看会发生什么。理想情况下它应该在那里。 @Borodin我不希望我的正则表达式匹配以“。”开头的文件。或以“.pl”结尾。

标签: perl recursion directory


【解决方案1】:

您无法打开该文件,因为 $file 在那个时间点恰好是一个目录,因此您需要为此添加一个检查。

在打开文件进行阅读时,可能值得添加 or die 语句。

还要注意File::Find$_ 设置为当前文件名,因此生成$file 的5 行实际上是不必要的。

【讨论】:

  • 我也是这么想的。但是后来我转到了 File::Find (perldoc.perl.org/File/Find.html) 的 perldocs 页面,我从那里了解到 find() 会自动为找到的每个目录执行 chdir() ,然后继续从那里搜索文件。由于 finddepth 与 find 基本相同,因此它应该可以正常工作。
  • chdir() 的观点是正确的。这就是为什么您可以只对文件名调用 open 而不需要完整路径的原因。
  • 在这种情况下,perl 不应该处理这个问题吗?我在文件名上调用 open,即 $fullname = $name.$suffix,而不是文件路径。
  • 澄清一下,主要问题是$file 在遍历树时可以是文件名或目录名。但是您不知道它何时是目录名称。使用-f-d 文件测试来检查这一点。
【解决方案2】:

您的代码存在一些问题。

  • use warnings 优于命令行-w

  • 在第一次使用时声明变量,而不是在子例程顶部的块中

  • 使用open的三参数形式,以及词法文件句柄

  • 在检查open 调用的状态时,将内置变量$! 放入die 字符串中,以便您知道为什么打开失败

  • 不要将标量变量放在双引号内。这可能是不必要的,并且在某些情况下可能会破坏您的代码。极不可能做任何你想做的事情

您的程序的这种重写使用use autodie 来避免需要显式的open ... or die $! 语句。它使用rename 来更改文件的名称,而不是复制它并覆盖原始文件。

我没有将整个文件读入内存,而是打开重命名的文件并逐行读取,编辑每一行并将其写入新文件

我已经编写了它,以便它忽略以点开头或以.pl 结尾的文件 - 我希望这是对的。我也很怀疑您的替换 s/[^-]\d+/ **/g 会查找前面不是连字符的字符的数字序列;是这样吗?

#!/usr/local/bin/perl

use strict;
use warnings;

use autodie;
no autodie 'unlink';

use File::Find 'finddepth';

finddepth(\&file_list, @ARGV);

sub file_list {

  return unless -f;
  return if /^\./ or /\.pl$/;

  my $file = $_;
  my $orig = "$file.orig";

  unlink $orig;
  rename $file, $orig;

  open my $infh, '<', $orig;
  open my $outfh, '>', $file;

  while (my $line = <$infh>) {
    if ($line =~ /^\s+Error:/) {
      $line =~ s/[^-]\d+/ **/g
    }
    print $outfh $line;
  }

  close $outfh;
}

【讨论】:

  • 我不知道 -f 文件测试。添加 -f 测试后,我的代码运行良好。谢谢。
  • 很高兴为您提供帮助。我希望你已经注意到我的其他观点,因为它们是可以帮助你编写好的、可靠的代码的实践。
  • 是的,我有。至于我的替换,我试图避免替换负数。为什么你认为它是可疑的?我刚刚开始使用 perl,所以我还在为正则表达式苦苦挣扎。
  • 嗯,好的。您想要“前面没有连字符的数字”,这与“前面没有连字符的数字”不同。该正则表达式的问题是双重的。首先,它不会匹配字符串开头的数字,因为它前面没有任何内容,其次它在替换中包含前面的字符,因此它将被删除并替换为空格,无论它最初是什么。你想要的是一个消极的后视 - s/(?&lt;!-)\d+/**/
  • 您的正则表达式有一个问题。它只是替换前面有“-”的单个数字。例如,-4 被忽略,但在运行脚本后 -54 变为 -5**。因此,我将您建议的正则表达式修改为 s/((?
猜你喜欢
  • 2016-07-22
  • 1970-01-01
  • 2011-02-01
  • 1970-01-01
  • 2016-06-21
  • 2014-09-30
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
相关资源
最近更新 更多