使用glob返回旧值的Line Input运算符

时间:2017-06-30 23:36:31

标签: perl glob

以下摘录代码在perl 5.16.3及更早版本上运行时有一个奇怪的行为,其中对行输入操作符中的glob的后续调用会导致glob继续返回先前的值,而不是重新运行glob

#!/usr/bin/env perl

use strict;
use warnings;

my @dirs = ("/tmp/foo", "/tmp/bar");

foreach my $dir (@dirs) {    
    my $count = 0;
    my $glob = "*";
    print "Processing $glob in $dir\n";
    while (<$dir/$glob>) {
        print "Processing file $_\n";
        $count++;
        last if $count > 0;
    }
}

如果您将两个文件放在 / tmp / foo 中,并在 / tmp / bar 中放入一个或多个文件,并运行代码,我会得到以下输出:

  

在/ tmp / foo中处理*

     

处理文件/tmp/foo/foo.1

     

在/ tmp / bar中处理*

     

处理文件/tmp/foo/foo.2

我认为当whilelast之后终止时,第二次迭代中while的新调用会重新运行glob并给我列出的文件< em> / tmp / bar ,但我继续了解 / tmp / foo 中的内容。

它几乎就像角度算子glob的行为类似于预编译模式。我的假设是角度算子在符号表中创建一个文件句柄,它仍然在后台打开并在幕后重用,并且它的作用域是包含foreach,或者可能是整个子程序。

1 个答案:

答案 0 :(得分:5)

来自I/O Operators in perlop (我的重点)

  

(文件)glob仅在启动时评估其(嵌入)参数       新名单。 所有值必须在重新开始之前阅读。在列表中       上下文,这并不重要,因为你会自动获得它们       无论如何。但是,在标量上下文中,运算符返回下一个值       每次调用时,或undef列表用完时。

由于此处在标量上下文中调用<>,并且在第一次迭代后退出循环last,因此下次输入时它将继续从原始列表中读取。

在评论中澄清说,这背后有实际需要:只处理目录中的一些文件而永远不会返回所有文件名,因为可能有很多文件名。

因此,从glob分配到列表并使用它,或者更好地使用for而不是while评论ysth,这对我们没有帮助因为它返回了一个巨大的列表。

我还没有找到一种方法让glob(文件名模式使用<>)删除并重新生成列表一旦生成它,而没有到达它先结束。 显然,运算符的每个实例都有自己的列表。因此,在<>循环中使用另一个while,希望以任何方式重置它,即使使用相同的模式,也不会影响while (<$glob>)中迭代的列表

请注意,使用diewhile中的eval)突破循环并不会有任何帮助;下次我们来while时,同样的列表会继续。将其包装在封闭中

sub iter_glob { my $dir = shift; return sub { scalar <"$dir/*"> } }

for my $d (@dirs) {
    my $iter = iter_glob($d);
    while (my $f = $iter->()) {
        # ...
    }
}
遇到了同样的命运;原始列表一直在使用。

然后解决方案是改为使用readdir