【问题标题】:Finding files with Perl使用 Perl 查找文件
【发布时间】:2013-07-19 06:15:56
【问题描述】:

File::Find wanted 子程序

这个问题比原来的标题(“子程序的原型和前向声明”!)要简单得多。我希望答案,无论多么简单,都能帮助我理解子例程/函数、原型和范围以及File::Find 模块。

使用 Perl,子程序几乎可以出现在任何地方,您通常不需要进行前向声明(除非子程序声明了一个原型,我不确定如何在 Perl 中以“标准”方式进行) .对于我通常使用 Perl 所做的事情,这些不同的运行方式之间几乎没有区别 somefunction

sub somefunction;  # Forward declares the function
&somefunction; 
somefunction();
somefunction;   # Bare word warning under `strict subs`

我经常使用find2perl 生成代码,我将这些代码植入/破解到脚本的某些部分。这很可能是不好的风格,现在我的脏衣服是公开的,但就这样吧:-) 对于File::Findwanted 函数是必需的子例程 - find2perl 创建它并将sub wanted; 添加到生成的脚本中创建。有时,当我编辑脚本时,我会从sub wanted 中删除“sub”,它最终会变成&wanted;wanted();。但是如果没有sub wanted; 前向声明表,我会收到以下警告:

Use of uninitialized value $_ in lstat at findscript.pl line 29

我的问题是:为什么会发生这种情况,这是一个真正的问题吗?这“只是一个警告”,但我想更好地理解它。

  • 文档和代码说 $_ 本地化在 sub wanted {} 内部。如果我使用wanted(); 而不是sub wanted;,为什么它会未定义?
  • wanted 是否在某处使用原型?我在Find/File.pm 中遗漏了什么明显的东西吗?
  • 是不是因为wanted 返回了代码引用? (???)

我的猜测是前向声明表单以某种方式“初始化”wanted,这样第一次使用就没有空的默认变量。我想这就是原型——即使是 Perl 原型,比如它们存在的——也可以工作的方式。我尝试通过搜索 Perl 源代码来了解在使用 sub function 而不是 function() 调用函数时 sub 正在做什么,但这可能超出了我的理解范围。

非常感谢任何加深(和加快)我对此理解的帮助。

编辑:这是我使用find2perl 的输出创建的recent example script here on Stack Overflow。如果你从sub wanted; 中删除sub,你应该会得到同样的错误。

编辑:正如我在下面的评论中指出的(但我也会在这里标记它):几个月来我一直在使用Path::Iterator::Rule 而不是File::Find。它需要perl >5.10,但我永远不必在具有奇怪、“永不升级”、5.8.* 唯一策略的站点上部署生产代码,因此Path::Iterator::Rule 已成为我永远不想放弃的模块之一。 Path::Class 也很有用。干杯。

【问题讨论】:

标签: perl find


【解决方案1】:

我不是 File::Find 的忠实粉丝。它只是不起作用正确find 命令不返回文件列表,因此您必须在 find 中使用 non-local 数组变量来捕获您找到的文件列表(不是好),或者把你的整个程序放在你的 wanted 子例程中(甚至更糟)。另外,单独的子例程意味着您的逻辑与您的find 命令是分开的。太丑了。

我所做的是将我的wanted 子例程内联到我的find 命令中。子程序与查找保持一致。另外,我的 non-local 数组变量现在只是我的find 命令的一部分,看起来还不错

以下是我处理File::Find 的方式——假设我想要具有.pl 后缀的文件:

my @file_list;
find ( sub {
    return unless -f;       #Must be a file
    return unless /\.pl$/;  #Must end with `.pl` suffix
    push @file_list, $File::Find::name;
}, $directory );

# At this point, @file_list contains all of the files I found.

这完全一样:

my @file_list;

find ( \&wanted, $directory );

sub wanted {
    return unless -f;
    return unless /\.pl$/;
    push @file_list, $File::Find::name;
}

# At this point, @file_list contains all of the files I found.

衬里看起来更好。而且,它将我的代码保持在一起。另外,我的 non-local 数组变量看起来并不那么怪异。

我也喜欢以这种特殊的方式利用 shorter 语法。通常,我不喜欢使用 推断的$_,但在这种情况下,它使代码更易于阅读。我原来的通缉犯是这样的:

sub wanted {
    my $file_name = $_;
    if ( -f $file_name and $file_name =~ /\.pl$/ ) {
        push @file_list, $File::Find::name;
    }
}

File::Find 使用起来并不难。你只需要记住:

  • 找到不想要的文件时,使用return 转到下一个文件。
  • $_ 包含没有目录的文件名,您可以使用它来测试文件。
  • 文件的全名是$File::Find::name
  • 文件的目录是$File::Find::dir

而且,最简单的方法是将您想要的文件推送到一个数组中,然后稍后在您的程序中使用该数组。

【讨论】:

  • 嗨 David W. 和 @tobyink 我接受 Davids 的回答,因为它没有回答我关于子例程的漫无边际的问题 - 它确实帮助我了解了更多关于 File::Find 和查找文件的信息.干杯并感谢你们俩。
【解决方案2】:

sub wanted; 中删除sub 只会调用wanted 函数,而不是前向声明。

但是,wanted 函数并未设计为直接从您的代码中调用 - 它设计为由 File::Find 调用。 File::Find 在调用它之前会做一些有用的事情,比如填充$_

这里不需要前向声明wanted,但是如果你想删除前向声明,删除整个sub wanted; 行——不仅仅是sub这个词。

【讨论】:

  • 谢谢。我还不能说理解前向声明是如何工作的,但我可以看到这里没有必要,并且它是产生未初始化$_ 错误的普通函数调用。
  • subs 的前向声明很少有用。几乎唯一有用的情况是您调用具有有趣原型的子程序,并且需要在编译子程序本身之前编译该调用。如果你不明白我的意思,那没关系 - 只是不要转发声明 subs,并避免原型,你通常会没事的。
【解决方案3】:

我建议使用File::Find::Wanted 中的find_wanted 函数,而不是File::Find

find_wanted 接受两个参数:

  • 一个为您想要的任何文件名返回 true 的子例程。
  • 您正在搜索的文件列表。

find_wanted 返回一个数组,其中包含它找到的文件名列表。


我使用如下代码在计算机上的某些目录中查找所有 JPEG 文件:

my @files = find_wanted( sub { -f && /\.jpg$/i }, @dirs );



一些语法的解释,对于那些可能需要它的人:

sub {...} 是一个匿名子例程,其中... 被替换为子例程的代码。
-f 检查文件名是否引用了“普通文件”
&& 是布尔值和
/\.jpg$/i 是一个正则表达式,用于检查文件名是否以.jpg 结尾(不区分大小写)。
@dirs 是一个包含要搜索的目录名称的数组。也可以搜索单个目录,在这种情况下,标量也可以工作(例如$dir)。

【讨论】:

    【解决方案4】:

    为什么不使用open 并调用shell find?用户可以将$findcommand(如下)编辑为他们想要的任何内容,或者可以根据传递给脚本的参数和选项实时定义它。

    #!/usr/bin/perl
    use strict; use warnings; 
    my $findcommand='find . -type f -mtime 0';
    open(FILELIST,"$findcommand |")||die("can't open $findcommand |");
    my @filelist=<FILELIST>;
    close FILELIST;
    my $Nfilelist = scalar(@filelist);
    print "Number of files is $Nfilelist \n";
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多