【问题标题】:Issues with function calls for search routine搜索例程的函数调用问题
【发布时间】:2014-10-30 07:09:20
【问题描述】:

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

/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 年表示文件最后修改的年份。

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

参考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. 如果我在func 之外定义函数upToDateFiles,我会收到此错误:Execution of ./script.pl aborted due to compilation errors. 我认为这是因为变量未在func 之外定义。是否可以改变这一点并仍然获得所需的输出?

【问题讨论】:

  • 您可以使用my $upToDateFiles = sub { ... 代替sub upToDateFiles { ...,在find 调用之前定义,并将其用作find($upToDateFiles, ...
  • @Qtax 谢谢,这就像一个魅力!如果您能详细说明/解释一下并提出答案,我会很高兴。那时我也可以投票:)

标签: perl function find


【解决方案1】:

对于初学者 - 在另一个子中嵌入一个子是相当讨厌的。如果你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.。所以“较新”的文件是较低的数字,而不是较高的。 (所以在上面做了相应的修改)。

【讨论】:

  • 这不起作用。它给出了一个警告,而且输出不是所需的。
  • 避免嵌套 subs 的提示很好。我在第 3 点要求它。我仍然不知道如何正确地做到这一点。
  • 由于编译错误而中止,之后得到错误,它给了你其他警告。打开use diagnostics,看看它说了什么。
  • 我刚刚复制了您的代码(带有诊断功能)并尝试了它...它运行不正确,因为 mtime 未定义并且输出的“最新”文件实际上不是最新的。这段代码对你有用吗?
  • 您还可以通过将 find 处理程序包装在一个闭包中来解决您在 2) 和 3) 中确定的问题,然后使用任何必要的 args 调用该处理程序,即find(sub { upToDateFiles(\%times, @_) }, "/dir/here");
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-29
相关资源
最近更新 更多