函数调用搜索例程的问题

时间:2014-10-29 08:05:44

标签: perl function find

我的目标是以递归方式对特定文件进行多次搜索。所以我有这些文件:

/dir/here/tmp1/recursive/foo2013.log
/dir/here/tmp1/recursive/foo2014.log
/dir/here/tmp2/recursive/foo2013.log
/dir/here/tmp2/recursive/foo2014.log

2013年和2014年在哪一年说文件最后修改过。

然后我想为每个目录树找到更多最新文件(foo2014.log)(同样地tmp1tmp2)。

参考this answer我在 script.pl 中有以下代码:

#!/usr/bin/perl
use strict;
use warnings;
use File::Find;

func("tmp1");
print "===\n";
func("tmp2");

sub func{
    my $varName = shift;
    my %times;
    find(\&upToDateFiles, "/dir/here");

    for my $dir (keys %times) {
        if ($times{$dir}{file} =~ m{$varName}){
            print $times{$dir}{file}, "\n";
            # do stuff here
        }
    }

    sub upToDateFiles {
        return unless (-f && /^foo/);
        my $mod = -M $_;
        if (!defined($times{$File::Find::dir})
         or $mod < $times{$File::Find::dir}{mod})
        {
            $times{$File::Find::dir}{mod} = $mod;
            $times{$File::Find::dir}{file} = $File::Find::name;
        }
    }
}

这将给我这个输出:

Variable "%times" will not stay shared at ./script.pl line 25.
/dir/here/tmp1/recursive/foo2014.log
===

我有三个问题:

  1. 为什么函数func的第二次调用不像第一次调用那样?变量只是在函数范围内定义,为什么我会受到干扰?

  2. 为什么我会收到变量%times的通知,如何摆脱它?

  3. 如果我在upToDateFiles之外定义函数func,我会收到此错误:Execution of ./script.pl aborted due to compilation errors.我认为这是因为变量不是在func之外定义的{1}}。是否可以更改此设置并仍然获得所需的输出?

1 个答案:

答案 0 :(得分:3)

对于初学者 - 在另一个sub中嵌入sub是相当讨厌的。如果你use diagnostics;,你会得到:

(W closure) An inner (nested) named subroutine is referencing a
lexical variable defined in an outer named subroutine.

When the inner subroutine is called, it will see the value of
the outer subroutine's variable as it was before and during the *first*
call to the outer subroutine; in this case, after the first call to the
outer subroutine is complete, the inner and outer subroutines will no
longer share a common value for the variable.  In other words, the
variable will no longer be shared.

This problem can usually be solved by making the inner subroutine
anonymous, using the sub {} syntax.  When inner anonymous subs that
reference variables in outer subroutines are created, they
are automatically rebound to the current values of such variables.

这与您遇到的问题直接相关。尽量避免嵌套你的潜艇,你就不会遇到这个问题。看起来你似乎想要比你需要的要复杂得多。你考虑过这样的事情:

#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

use File::Find;

my %filenames;

sub compare_tree {
    return unless -f && m/^foo/;

    my $mtime = -M $File::Find::name;

    if ( !$filenames{$_} || $mtime < $filenames{$_}{mtime} ) {
        $filenames{$_} = {
            newest => $File::Find::name,
            mtime  => $mtime,
        };
    }
}

find( \&compare_tree, "/dir/here" );

foreach my $filename ( keys %filenames ) {
    print "$filename has newest version path of:", $filenames{$filename}{newest}, "\n";
    print "$filename has newest mtime of:",        $filenames{$filename}{mtime},  "\n";
}

我还注意到 - 你似乎正在使用$File::Find::dir - 根据你所描述的你正在做的事情,这对我来说是错误的。同样 - 你在相同的目录结构上运行find两次,这不是一种非常有效的方法 - 非常大的发现是昂贵的操作,所以加倍所需的工作并不好。

编辑:忘记-M是:-M Script start time minus file modification time, in days.。所以'较新'的文件是较低的数字,而不是更高的数字。 (因此已在上面进行了修改)。