Почему Perl file glob () не работает вне цикла в скалярном контексте? - PullRequest
10 голосов
/ 14 апреля 2010

Согласно документации Perl о глобализации файлов, оператор <*> или функция glob () при использовании в скалярном контексте должны перебирать список файлов, соответствующих указанному шаблону, возвращая следующее имя файла каждый раз, когда оно называется или undef, когда больше нет файлов.

Но итерационный процесс работает только внутри цикла. Если он не находится в цикле, то кажется, что он начинается сначала, прежде чем все значения будут прочитаны.

Из документов Perl:

В скалярном контексте glob перебирает такие расширения имени файла, возвращая undef, когда список исчерпан.

http://perldoc.perl.org/functions/glob.html

Однако в скалярном контексте оператор возвращает следующее значение каждый раз, когда он вызывается, или undef, когда список заканчивается.

http://perldoc.perl.org/perlop.html#I/O-Operators

Пример кода:

use warnings;
use strict;

my $filename;

# in scalar context, <*> should return the next file name
# each time it is called or undef when the list has run out

$filename = <*>;
print "$filename\n";
$filename = <*>;      # doesn't work as documented, starts over and
print "$filename\n";  # always returns the same file name
$filename = <*>;
print "$filename\n";

print "\n";

print "$filename\n" while $filename = <*>; # works in a loop, returns next file
                                           # each time it is called

В каталоге с 3 файлами ... file1.txt, file2.txt и file3.txt приведенный выше код выведет:

file1.txt
file1.txt
file1.txt

file1.txt
file2.txt
file3.txt

Примечание. Фактический сценарий perl должен находиться за пределами тестового каталога, иначе вы увидите имя файла сценария в выходных данных.

Я что-то здесь не так делаю или это так должно работать?

Ответы [ 4 ]

9 голосов
/ 14 апреля 2010

Вот способ уловить магию состояния оператора глобуса <> в объект, которым вы можете манипулировать обычным способом: анонимные подпрограммы (и / или замыкания)!

sub all_files {
    return sub { scalar <*> };
}

my $iter = all_files();
print $iter->(), "\n";
print $iter->(), "\n";
print $iter->(), "\n";

или, возможно:

sub dir_iterator {
    my $dir = shift;
    return sub { scalar glob("$dir/*") };
}
my $iter = dir_iterator("/etc");
print $iter->(), "\n";
print $iter->(), "\n";
print $iter->(), "\n";

Опять же, я склонен подать это под "любопытство". Не обращайте внимания на эту особую странность glob() / <> и используйте opendir / readdir, IO :: All / readdir или File :: Glob вместо: )

5 голосов
/ 14 апреля 2010

Следующий код, похоже, также создает 2 отдельных экземпляра итератора ...

for ( 1..3 )
{
   $filename = <*>;
   print "$filename\n" if defined $filename;
   $filename = <*>;
   print "$filename\n" if defined $filename;
}

Думаю, я вижу логику, но она противоречит интуиции и противоречит документации. В документах ничего не говорится о необходимости быть в цикле, чтобы итерация работала.

3 голосов
/ 14 апреля 2010

Также от perlop:

Глобус (файл) оценивает свой (встроенный) аргумент только тогда, когда он начинает новый список.

Вызов glob создает список, который либо возвращается целиком (в контексте списка), либо извлекается по одному элементу за раз (в скалярном контексте). Но каждый вызов glob создает отдельный список.

0 голосов
/ 14 апреля 2010

(Вычеркивая мою ржавую память о Perl ...) Я думаю, что несколько лексических экземпляров <*> рассматриваются как независимые вызовы glob, тогда как в цикле while вы вызываете один и тот же "экземпляр" (независимо от того, что средства).

Представьте себе, например, если вы сделали это:

while (<*>) { ... }
...
while (<*>) { ... }

Вы, конечно, не ожидаете, что эти два вызова будут мешать друг другу.

...