具有 glob 返回旧值的行输入运算符

Line Input operator with glob returning old values

以下摘录代码,当 运行在 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,并且 运行 代码,我得到以下输出:

Processing * in /tmp/foo

Processing file /tmp/foo/foo.1

Processing * in /tmp/bar

Processing file /tmp/foo/foo.2

我认为当 whilelast 之后终止时,第二次迭代 while 的新调用将重新 运行 glob 和给我列出的文件 /tmp/bar,但我得到的是 /tmp/foo.[=16= 中内容的延续]

角度运算符 glob 几乎就像预编译模式一样。我的假设是角度运算符正在符号 table 中创建一个文件句柄,它仍然打开并在幕后被重用,并且它的范围是包含 foreach,或者可能是整个子例程。

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

A (file)glob evaluates its (embedded) argument only when it is starting a new list. All values must be read before it will start over. In list context, this isn't important because you automatically get them all anyway. However, in scalar context the operator returns the next value each time it's called, or undef when the list has run out.

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


在评论中阐明了这个任务背后的实际需求:只处理目录中的一些文件,从不return所有文件名,因为那里可以很多。

因此,从 glob 分配给一个列表并使用它,或者更好的是使用 for 而不是 ysth 评论的 while,没有帮助这里 return 是一个巨大的列表。

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

请注意,用 die 跳出循环(在 eval 中使用 while)也无​​济于事;下次我们到达 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 代替。