Почему glob Perl возвращает undef для каждого второго вызова?

Я не обязательно ищу лучший способ сделать это, скорее, будут очень признательны за объяснения вывода. Недавно старший программист спросил меня, почему его код работает, но только в одном случае. Я пришел к выводу, что это срабатывало в каждом втором случае. Вот мой пример:

#!/usr/bin/perl -w
use strict;

my @list_env_vars = (
    '$SERVER',
    '$SERVER',
    '$SERVER',
    '$SERVER',
    '$SERVER',
    '$SERVER',
);

foreach (@list_env_vars){
    print "$_ = ".glob()."\n";
}

какой вывод для Perl 5.004:

$SERVER = UNIX_SERVER
$SERVER =
$SERVER = UNIX_SERVER
$SERVER =
$SERVER = UNIX_SERVER
$SERVER =

или вывод для Perl 5.10:

$SITE = $SITE
Use of uninitialized value in concatenation (.) or string at glob_test.pl line 14.
$SITE =
$SITE = $SITE
Use of uninitialized value in concatenation (.) or string at glob_test.pl line 14.
$SITE =
$SITE = $SITE
Use of uninitialized value in concatenation (.) or string at glob_test.pl line 14.
$SITE =

Я лично никогда не использовал glob() таким образом, поэтому я был плохо подготовлен, чтобы ответить ему. Я прочитал документацию perldoc glob и следил за File::Glob на этой странице и по-прежнему не может найти ничего, что могло бы объяснить вывод. Любая помощь приветствуется.


person Akers    schedule 13.08.2009    source источник
comment
Вероятно, будет использоваться perl 5.004 из-за ограничений на машины, на которые он будет распространяться.   -  person Akers    schedule 14.08.2009


Ответы (3)


glob в скалярном контексте:

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

In

foreach (@list_env_vars){
    print "$_ = ".glob()."\n";
}

glob() на самом деле есть glob($_). Каждая итерация $_ содержит строку $SERVER. Учитывая, что переменная окружения не меняется, $SERVER расширяется до той же строки. Эта строка возвращается впервые. Далее список исчерпывается, поэтому возвращается undef. В третий раз начинаем заново. ...

Пояснение: не имеет значения, что аргумент второго вызова совпадает с аргументом первого вызова, так как нет возможности сбросить итератор glob.

Вы можете увидеть это более четко, используя следующий пример (текущий каталог содержит файлы '1.a', 1.b', '2.a' и '2.b'):

#!/usr/bin/perl -w
use strict;

my @patterns = (
    '*.a',
    '*.b',
);

for my $v ( @patterns ) {
    print "$v = ", scalar glob($v), "\n";
}

Выход:

C:\Temp> d
*.a = 1.a
*.b = 2.a

Я бы рекомендовал обращаться к переменным окружения через хэш %ENV:

my @list_env_vars = ($ENV{SERVER}) x 6;

or

my @list_env_vars = @ENV{qw(HOME TEMP SERVER)};
person Sinan Ünür    schedule 13.08.2009
comment
Мне все еще трудно понять, почему это работает каждый раз, но спасибо за ваш ответ. - person Akers; 14.08.2009
comment
@Akers, вы даете glob выражение, которое расширяется до одной уникальной строки. Сначала он возвращает вам это, затем возвращает undef. Как говорится в документации. - person Sinan Ünür; 14.08.2009
comment
'Разве этот человек не знает, что он может получить доступ к значению переменной окружения через $ENV{SERVER}? Как насчет, да, на самом деле, я предложил использовать $ENV{}, мне просто было любопытно, как это когда-либо работало, я никогда раньше не использовал glob таким образом. Я просто использовал несколько версий одного и того же env var, чтобы доказать свою точку зрения. Неважно, используете ли вы 6 разных переменных, вы все равно получите тот же результат. Любая другая переменная печатает. - person Akers; 14.08.2009
comment
@Akers, невозможно сказать glob сбросить свой итератор. Таким образом, последующие вызовы с другими значениями не будут иметь значения, пока не будут исчерпаны совпадения для первого вызова. - person Sinan Ünür; 14.08.2009
comment
Это мой друг, это то, что я искал, большое спасибо! - person Akers; 14.08.2009
comment
Это действительно хорошее объяснение, я, вероятно, пошлю своему старшему программисту ссылку на эту страницу, это такое хорошее объяснение. Я хотел бы удвоить голосование, мне это так нравится, поэтому я также проголосовал за ваши комментарии. - person Akers; 14.08.2009
comment
Спасибо. Тем временем я нашел следующее обсуждение, которое может оказаться полезным: groups.google.com/group/perl.perl5.porters/browse_thread/thread/ - person Sinan Ünür; 14.08.2009

Между прочим, причина, по которой в 5.004 вы получаете расширение переменной, а в 5.10 вы просто возвращаете литеральную строку, заключается в том, что в старом Perl glob() выполнялась системной оболочкой, которая в качестве побочного эффекта выполняет расширение переменной. Начиная с perl 5.6, glob() использует модуль File::Glob, который выполняет всю работу сам, без оболочки, и не расширяет переменные среды (для чего glob никогда не предназначался). %ENV - это правильный способ добраться до окружающей среды.

person hobbs    schedule 14.08.2009
comment
спасибо, это было то, что я подозревал о старой версии glob, но не знал, где найти это для проверки. - person Akers; 22.08.2009

Заметки о старом поведении, вики для вашего удобства (и чтобы у меня был полный диапазон разметки и не было ограничения в 500 символов):

Тот факт, что glob и <*globbything*> изменились в 5.6, мимоходом упоминается в документах (perl56delta, perlop, -f glob), но единственный реальный источник информации о том, как это работало, — это версия perlop до 5.6. Вот соответствующий бит из 5.005:

Пример:

while (<*.c>) {
    chmod 0644, $_;
}

эквивалентно

open(FOO, "echo *.c | tr -s ' \t\r\f' '\\012\\012\\012\\012'|");
while (<FOO>) {
    chop;
    chmod 0644, $_;
}

Собственно, так оно и реализовано в настоящее время. (Это означает, что он не будет работать с именами файлов, содержащими пробелы, если на вашем компьютере не установлен csh(1).)

Хех, это довольно злая штука. В любом случае, если вы когда-нибудь захотите обратиться к таким старым документам Perldoc, просто зайдите на search.cpan.org, откройте дистрибутив perl, используйте раскрывающийся список, чтобы выбрать старую версию, а затем перейдите к нужному документу. Сам perl на самом деле не подлежит «очистке» от CPAN; в настоящее время все, начиная с 5.004, доступно без нажатия BackPan.

person Community    schedule 22.08.2009