Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does ${fh} behave differentially than $fh when reading line by line?

Tags:

perl

This 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;

outputs:

GLOB(0x5bcc2a0)

Why is this the output?

If I change <${fh}> to <$fh>, it will print some_file.txt line by line as expected. I thought the braces could be used to delimit variable names, and that my ${var} would be the same as my $var.

Are there other scenarios where adding {} around a variable name causes problems?

I tried on Perl 5.8.8 on Red Hat and Cygwin Perl 5.14.

like image 423
hmatt1 Avatar asked Jan 15 '16 22:01

hmatt1


2 Answers

From the section on <> in perlop:

If what the angle brackets contain is a simple scalar variable (for example, $foo), then that variable contains the name of the filehandle to input from, or its typeglob, or a reference to the same. For example:

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

If what's within the angle brackets is neither a filehandle nor a simple scalar variable containing a filehandle name, typeglob, or typeglob reference, it is interpreted as a filename pattern to be globbed, and either a list of filenames or the next filename in the list is returned, depending on context. This distinction is determined on syntactic grounds alone. That means <$x> is always a readline() from an indirect handle, but <$hash{key}> is always a glob(). That's because $x is a simple scalar variable, but $hash{key} is not--it's a hash element. Even <$x > (note the extra space) is treated as glob("$x "), not readline($x).

You can see this with 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
like image 51
ThisSuitIsBlackNot Avatar answered Oct 23 '22 20:10

ThisSuitIsBlackNot


  • <> means readline(ARGV)
  • <IDENTIFIER> means readline(IDENTIFIER)
  • <$IDENTIFIER> means readline($IDENTIFIER)
  • <...> (anything else) means glob(qq<...>)

So,

  • <$fh> means readline($fh)
  • <${fh}> means glob(qq<${fh}>), which is the same as glob("$fh").

glob is used to generate a number of strings or file names from a pattern.

In this case, the stringification of the file handle is GLOB(0x5bcc2a0). This is being passed to glob, but none of those characters have a special meaning for glob, so glob simply returns that string.

like image 34
ikegami Avatar answered Oct 23 '22 22:10

ikegami