Почему ${fh} ведет себя по-разному, чем $fh, при чтении строки за строкой?

Этот script:

#!/usr/bin/perl
use strict;
use warnings;

my ${fh};

open($fh, '<', "some_file.txt") or die "fail\n";

while(my $line = <${fh}> ) {
    print $line;
}
close $fh;

выходы:

GLOB(0x5bcc2a0)

Почему это вывод?

Если я изменил <${fh}> на <$fh>, он будет печатать some_file.txt по очереди, как ожидалось. Я думал, что скобки могут использоваться для разграничения имен переменных и что my ${var} будет таким же, как my $var.

Существуют ли другие сценарии, в которых добавление {} вокруг имени переменной вызывает проблемы?

Я пробовал на Perl 5.8.8 на Red Hat и Cygwin Perl 5.14.

Ответ 1

В разделе <> в perlop:

Если то, что содержат угловые скобки, является простой скалярной переменной (например, $foo), то эта переменная содержит имя дескриптора файла для ввода или его типglob или ссылку на него. Например:

$fh = \*STDIN;
$line = <$fh>;

Если то, что внутри угловых скобок не является ни дескриптором файла, ни простой скалярной переменной, содержащей имя дескриптора файла, typeglob или ссылку на типglob, интерпретируется как шаблон имени файла, который должен быть globbed, и либо список имен файлов, либо следующее имя файла в списке возвращается, в зависимости от контекста. Это различие определяется только по синтаксическим признакам. Это означает, что <$x> всегда является readline() от косвенного дескриптора, но <$hash{key}> всегда glob(). Это потому, что $x - простая скалярная переменная, но $hash{key} не является хеш-элементом. Даже <$x > (обратите внимание на дополнительное пространство) рассматривается как glob("$x "), а не readline($x).

Вы можете увидеть это с помощью B:: Concise:

$ perl -MO=Concise -e'<$fh>'
5  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 1 -e:1) v:{ ->3
4     <1> readline[t1] vK*/1 ->5
-        <1> ex-rv2sv sK/1 ->4
3           <$> gvsv(*fh) s ->4
-e syntax OK

$ perl -MO=Concise -e'<${fh}>'
6  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 70 -e:1) v:{ ->3
5     <@> glob[t1] vK/1 ->6
-        <0> ex-pushmark s ->3
-        <1> ex-rv2sv sK/1 ->4
3           <$> gvsv(*fh) s ->4
4        <$> gv(*_GEN_0) s ->5
-e syntax OK

Ответ 2

  • <> означает readline(ARGV)
  • <IDENTIFIER> означает readline(IDENTIFIER)
  • <$IDENTIFIER> означает readline($IDENTIFIER)
  • <...> (что-либо еще) означает glob(qq<...>)

Итак,

  • <$fh> означает readline($fh)
  • <${fh}> означает glob(qq<${fh}>), что совпадает с glob("$fh").

glob используется для создания нескольких строк или имен файлов из шаблона.

В этом случае строкой дескриптора файла является GLOB(0x5bcc2a0). Это передается в glob, но ни один из этих символов не имеет особого значения для glob, поэтому glob просто возвращает эту строку.