以下摘录代码在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
我认为当while
在last
之后终止时,第二次迭代中while
的新调用会重新运行glob并给我列出的文件< em> / tmp / bar ,但我继续了解 / tmp / foo 中的内容。
它几乎就像角度算子glob的行为类似于预编译模式。我的假设是角度算子在符号表中创建一个文件句柄,它仍然在后台打开并在幕后重用,并且它的作用域是包含foreach
,或者可能是整个子程序。
答案 0 :(得分: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
。