daiyu has asked for the wisdom of the Perl Monks concerning the following question:

hello: this is coming from a novice so pls be kind. want to key a set of filehandles in a hash, along the following lines:

open $hash{'t'},"test.txt"; ... @lines = <$hash{'t'}>; print @lines; # end up with a GLOB pointer, # not the contents of the file close $hash{'t"];

however when I code this way I get a GLOB reference. In order to print out the actual contents of the file I have to gimmick the filehandle as follows:

open $hash{'t'},"test.txt"; $a = $hash{'t'}; # pass the hash value # to another scalar @lines = <$a>; # and suddenly grabbing the @lines print @lines; # results in correct output! ...

Granted this is an easy enough workaround in practice. But I would like to understand why it appears to be necessary to pass the filehandle in $hash{'t'} to $a before <$a> returns correct output to @lines. This is not great way to do it, and I suspect there's some form of dereferencing that could eliminate the need for the intermediary assignment to $a. Thanks for any thoughts!

Replies are listed 'Best First'.
Re: why doesn't this work
by chromatic (Archbishop) on Aug 24, 2011 at 01:26 UTC

    Perl 5 overloads the same punctuation for glob and readline, so the parser has to guess at whether you meant "read lines from file" or "search directory for files matching this pattern". In this case, it guessed incorrectly.

    (I haven't read this part of the parser today, so this is an educated guess.)

Re: why doesn't this work
by CountZero (Bishop) on Aug 24, 2011 at 08:39 UTC
    As always the holy docs hold the solution!

    From the Book of I/O Operators:

    If what the angle brackets contain is a simple scalar variable (e.g., <$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).
    So there is no guessing or DWIM involved: it is actually a very simple rule.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: why doesn't this work
by charlesboyo (Beadle) on Aug 24, 2011 at 06:31 UTC

    Invoking readline directly, instead of using the <> operator should work just fine.

    @lines = readline($hash{'t'});
Re: why doesn't this work
by RichardK (Parson) on Aug 24, 2011 at 09:20 UTC

    Obviously I don't know what problem you're solving, so this may not be relevant, but is there any advantage to storing the file handles if you are just going to read all the lines?

    You could just keep the file names in your hash and use File::Slurp . something like :-

    use File::Slurp; my %hash; $hash{t} = 'filename'; my @lines = read_file( $hash{t} );

      RichardK:

      If you're working with large files, it may be impossible to read all the lines into memory.

      ...roboticus

      When your only tool is a hammer, all problems look like your thumb.

Re: why doesn't this work
by Anonymous Monk on Aug 24, 2011 at 05:03 UTC