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

问题描述 投票:2回答:1

以下摘录代码在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中放入一个或多个文件,并运行代码,我会得到以下输出:

处理* in / tmp / foo

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

处理* in / tmp / bar

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

我认为当whilelast之后终止时,while在第二次迭代时的新调用将重新运行glob并给我列出的文件/ tmp / bar,但我得到了/ tmp中的内容的延续/ foo中。

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

perl glob
1个回答
5
投票

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

(文件)glob仅在启动新列表时才计算其(嵌入)参数。必须先读取所有值,然后才能重新开始。在列表上下文中,这并不重要,因为无论如何都会自动获取它们。但是,在标量上下文中,运算符在每次调用时返回下一个值,或者在列表用完时返回undef

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


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

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

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

需要注意的是,用die(在while中使用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代替。

© www.soinside.com 2019 - 2024. All rights reserved.