使用Perl查找文件

时间:2013-07-19 20:27:49

标签: perl find

File::Find wanted 子程序

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

使用Perl,子程序几乎可以出现在任何地方,你通常不需要进行前向声明(除非sub声明了一个原型,我不知道如何在Perl中采用“标准”方式) 。对于我通常使用Perl做的事情,这些不同的运行方式somefunction

之间几乎没有区别
sub somefunction;  # Forward declares the function
&somefunction; 
somefunction();
somefunction;   # Bare word warning under `strict subs`

我经常使用find2perl来生成代码,我将这些代码整合到脚本的一部分中。这可能是糟糕的风格,现在我的脏衣服是公开的,但所以它是:-)对于File::Findwanted函数是必需的子例程 - find2perl创建它并添加{{ 1}}到它创建的结果脚本。有时,当我编辑脚本时,我会从sub wanted;中删除“sub”,最终会显示为sub wanted&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,以便第一次使用时没有空的默认变量。我想这就是原型 - 即使是Perl原型,如它们存在 - 也会起作用。我尝试使用Perl源代码来了解使用wanted而不是sub调用函数时sub function正在做什么,但这可能超出了我的目的。

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

编辑:这是我使用function()输出创建的recent example script here on Stack Overflow。如果您从find2perl中移除sub,则会收到相同的错误。

编辑:正如我在下面的评论中所指出的那样(但我也会在此处标记):几个月来我一直在使用Path::Iterator::Rule代替sub wanted; 。它需要File::Find,但我从不必在具有奇数,“永不升级”,perl >5.10仅限策略的网站上部署生产代码,因此5.8.*已成为我永远不想做的模块之一没有。同样有用的是Path::Class。欢呼声。

4 个答案:

答案 0 :(得分:12)

我不是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.

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

我也喜欢以这种特殊的方式利用更短的语法。通常,我不喜欢使用推断的$_ ,但在这种情况下,它使代码更容易阅读。我原来的通缉与此相同:

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

最简单的方法是将所需的文件推送到数组中,然后在程序中使用该数组。

答案 1 :(得分:5)

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

但是,wanted函数尚未设计为直接从您的代码中调用 - 它被设计为由File :: Find调用。 File :: Find会在调用它之前填充$_等有用的东西。

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

答案 2 :(得分:2)

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

find_wanted有两个参数:

  • 一个子例程,对于您想要的任何文件名都返回true。
  • 您要搜索的文件列表。

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


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

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


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

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

答案 3 :(得分:0)

为什么不使用open并调用外壳程序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";