具有glob的Line Input运算符返回旧值 [英] Line Input operator with glob returning old values

查看:71
本文介绍了具有glob的Line Input运算符返回旧值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The following excerpt code, when running on perl 5.16.3 and older versions, has a strange behavior, where subsequent calls to a glob in the line input operator causes the glob to continue returning previous values, rather than running the glob anew.

#!/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 中,然后运行代码,则会得到以下输出:

If you put two files in /tmp/foo and one or more in /tmp/bar, and run the code, I get the following output:

在/tmp/foo中处理*

Processing * in /tmp/foo

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

Processing file /tmp/foo/foo.1

在/tmp/bar中处理*

Processing * in /tmp/bar

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

Processing file /tmp/foo/foo.2

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

I thought that when the while terminates after the last, that the new invocation of the while on the second iteration would re-run the glob and give me the files listed /tmp/bar, but instead I get a continuation of what's in /tmp/foo.

这几乎就像角度运算符glob的行为就像预编译的模式一样.我的假设是,角度运算符正在符号表中创建一个文件句柄,该文件句柄仍处于打开状态并在后台重用,并且范围仅限于包含 foreach 或整个子例程的

It's almost like the angle operator glob is acting like a precompiled pattern. My hypothesis is that the angle operator is creating a filehandle in the symbol table that's still open and being reused behind the scenes, and that it's scoped to the containing foreach, or possibly the whole subroutine.

推荐答案

来自Perlop中的I/O运算符(我的重点)

(文件)全局变量仅在启动时才评估其(嵌入式)自变量新清单.必须先读取所有值,然后才能重新开始.在列表中上下文,这并不重要,因为您会自动将它们全部获取反正.但是,在标量上下文中,运算符将返回下一个值每次调用时,或在列表用完时 undef .

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 退出了循环,所以下次输入时,它将继续从原始列表.

Since <> is called in scalar context here and you exit the loop with last after the first iteration, the next time you enter it it keeps reading from the original list.

在注释中明确指出,此任务背后有实际需要:仅处理目录中的某些文件,并且绝不返回所有文件名,因为可以有很多文件名.

It is clarified in comments that there is a practical need behind this quest: process only some of the files from a directory and never return all filenames since there can be many.

因此,将 glob 分配给列表并使用它,或者更好的方法是使用 for 代替while ="https://stackoverflow.com/users/17389/ysth"> ysth ,在此无济于事,因为它返回了一个庞大的列表.

So assigning from glob to a list and working with it, or better yet using for instead of while as commented by ysth, doesn't help here as it returns a huge list.

我还没有找到一种方法来使 glob (具有文件名模式的<> 使用什么)拖放并在生成列表后立即重建列表,而没有首先到达终点.显然,该运算符的每个实例都有其自己的列表.因此,在 while 循环内使用另一个<> 希望以任何方式甚至使用相同的模式对其进行重置,都不会影响正在迭代的列表在 while(< $ glob>)中结束.

I haven't found a way to make glob (what <> with a filename pattern uses) drop and rebuild the list once it's generated it, without getting to its end first. Apparently, each instance of the operator gets its own list. So using another <> inside the while loop with the hope of resetting it, in any way and even with the same pattern, doesn't affect the list being iterated over in while (<$glob>).

仅需注意,使用 die (在 eval 中具有 while 的情况下)跳出循环也无济于事;下次我们来到 while 时,该列表将继续.将其包装在一个封闭的容器中

Just to note, breaking out of the loop with a die (with while in an eval) doesn't help either; the next time we come to that while the same list is continued. Wrapping it in a closure

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

for my $d (@dirs) {
    my $iter = iter_glob($d);
    while (my $f = $iter->()) {
        # ...
    }
}

命运相同原始列表一直在使用.

met with the same fate; the original list keeps being used.

然后的解决方案是改用 readdir .

The solution then is to use readdir instead.

这篇关于具有glob的Line Input运算符返回旧值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆