10

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;到它创建的结果脚本中。有时,当我编辑脚本时,我会从中删除“ subsub wanted,它最终会变成&wanted;or 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调用函数时正在执行的操作,但这可能超出了我的范围。sub functionfunction()

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

编辑:这是我使用输出创建的Stack Overflow 上最近的示例脚本。find2perl如果你从中删除subsub wanted;你应该得到同样的错误。

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

4

4 回答 4

17

我不是 File::Find 的忠实粉丝。它只是不能正常工作。该find命令不返回文件列表,因此您必须在您的文件中使用非本地数组变量find来捕获您找到的文件列表(不好),或者将整个程序放在您想要的子程序中(更糟)。另外,单独的子程序意味着您的逻辑与您的find命令是分开的。这只是丑陋的。

我所做的是将我的wanted子程序内联到我的find命令中。子程序与查找保持一致。另外,我的非本地数组变量现在只是我的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.

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

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

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.

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

于 2013-07-19T21:48:00.523 回答
5

删除subfromsub wanted;只是使它成为对wanted函数的调用,而不是前向声明。

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

此处无需前向声明wanted,但如果要删除前向声明,请删除整sub wanted;行 - 而不仅仅是单词sub

于 2013-07-19T21:09:42.350 回答
2

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

find_wanted接受两个参数:

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

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


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

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



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

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

于 2015-05-26T13:35:16.630 回答
0

为什么不使用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";
于 2019-11-19T00:39:59.130 回答