7.5. Storing Filehandles into Variables
7.5.1. Problem
You want to use a filehandle like a normal
variable so you can pass it to or return it from a function, store it
in a data structure, and so on.
7.5.2. Solution
The easiest way to get a
filehandle into a variable is to have open put it
there for you:
open(my $fh, "<", $filename) or die "$0: can''t open $filename: $!";
To store named filehandles into a variable or pass them into or out
of a function, use typeglob notation (*FH):
$variable = *FILEHANDLE; # save in variable
subroutine(*FILEHANDLE); # or pass directly
sub subroutine {
my $fh = shift;
print $fh "Hello, filehandle!\n";
}
7.5.3. Discussion
If
you pass an undefined scalar variable as the first argument to
open, Perl allocates an anonymous typeglob and
stores a reference to that typeglob in that scalar, effectively
creating filehandles on demand. Like all other references,
autovivified filehandles are subject to garbage collection, so this
code doesn''t leak a
filehandle:
{
open(my $fh, "< /etc/motd") or die;
local $/; # slurp mode
$text = <$fh>;
}
When Perl reaches the end of the block, $fh goes
out of scope. As explained earlier in the Introduction, because that variable contained the last
reference to the anonymous filehandle created by
open, the variable is garbage collected and the
filehandle implicitly closed.Autovivified filehandles, being anonymous and already held in
variables, don''t help you to understand how to pass named filehandles
as function parameters or store them in variables, including elements
of arrays or hashes. By named filehandles, we mean those of the form
FH, including all predefined handles, such as
STDIN and ARGV. So let''s look
at what FH is and how to extract a scalar value
from it to use for all of those things.Named filehandles used in:
print STDERR "stuff\n";
$input = <STDIN>;
open(TTY, "+<", "/dev/tty");
if (eof(ARGV)) { .... }
are names, not variables. They''re like subroutines in that way. This
makes them inconvenient to pass around or store into data structures.
Assuming you follow the prudent advice to compile all your code under
use strict by default, normally you can''t get away
with this:
$fh = SOMEHANDLE;
somefunc(SOMEHANDLE);
because, absent declarations to the contrary,
SOMEHANDLE is in both of these cases an unquoted
string, which is forbidden by use strict. Even if
you aren''t using strict subs, you''ll get into
trouble if you try to pass your handle into a subroutine that was
compiled under strict refs or in a different
package than the calling code was compiled in.The four named handles (STDERR,
STDIN, TTY, and
ARGV) we showed earlier didn''t require special
handling, but not because they are built-ins themselves;
TTY, in fact, is not. Rather, they were okay
because the built-in operations using them as arguments are all
prototyped to take a filehandle argument.So you must do one of two things. You could use a prototype for the
function as explained in Recipe 7.6.
Otherwise, you must use something that Perl will accept in lieu of a
filehandle name. Acceptable substitutes include strings, typeglobs,
references to typeglobs, and an I/O object, all of which may be
stored into variables or passed into a function for later use as
indirect filehandles.
somefunc( SOMEHANDLE ); # only w/ somefunc(*) proto
somefunc( "SOMEHANDLE" ); # an quoted string
somefunc( *SOMEHANDLE ); # a typeglob
somefunc( \*SOMEHANDLE ); # ref to a typeglob
somefunc( *SOMEHANDLE{IO} ); # an I/O object
Using a quoted string for the named handle has potential problems, as
already explained, although this can work if the code is careful
enough (again, see the next recipe). Better to use typeglob notation,
either directly using *SOMEHANDLE or by reference
using \*SOMEHANDLE:
somefunc(*SOMEHANDLE);
$fh = *SOMEHANDLE; # or indirectly via a variable
somefunc($fh);
print $fh "data\n";
Typeglob notation spares you quoting or qualifying the handle name.
It may help to conceptualize the asterisk as the type symbol for a
filehandle. Like the little colored balls from high school chemistry
that stood for atomic particles, it''s not really true, but it is a
convenient mental shorthand. By the time you understand where this
model breaks down, you won''t need it anymore.If you assign any of the four alternate forms for named filehandles
into a scalar variable, you can use that variable as an indirect
filehandle wherever you would use a named filehandle.
However, complex
expressions and subscripts into hashes or arrays cannot be used
directly with built-ins like print,
printf, or the line input operator. These are
syntactically illegal and won''t even compile:
@fd = (*STDIN, *STDOUT, *STDERR);
print $fd[1] "Type it: "; # WRONG
$got = <$fd[0]> # WRONG
print $fd[2] "What was that: $got"; # WRONG
With print and printf, you can
get around this by using a block, returning an expression where you
would place the filehandle:
print { $fd[1] } "funny stuff\n";
printf { $fd[1] } "Pity the poor %x.\n", 3_735_928_559;
Pity the poor deadbeef.
That block is a proper block in all senses, so you can put more
complicated code there. This sends the message out to one of two
places:
$ok = -x "/bin/cat";
print { $ok ? $fd[1] : $fd[2] } "cat stat $ok\n";
print { $fd[ 1 + ($ok || 0) ] } "cat stat $ok\n";
This is so-called "indirect object"
notation, discussed at length in Chapter 13. This
restriction against using anything but simple scalar variables in the
indirect object slot holds true for any sort of object. As with
user-created objects, infix arrow notation avoids syntactic snafus
here. If you have the IO::Handle module loaded, or anything that
inherits from it, use an expression that produces the filehandle as
though it were a proper object to invoke methods from that class:
$fd[1]->print("funny stuff\n");
($ok ? $fd[1] : $fd[2])->print("cat stat $ok\n");
This approach of treating print and
printf like object methods calls won''t work for
the line input operator. Assuming you''ve been storing typeglobs in
your structure as we did previously, the built-in
readline function reads records just as
<FH> does. Given the preceding
initialization of @fd, this would work:
$got = readline($fd[0]);
or, with IO::Handle available, you can use the
getline method:
$got = $fd[0]->getline( );
IO::Handle doesn''t replace the readline function
using just one method—it uses two, one per context. If you
prefer readline''s context-dependent behavior, you
could always do this, adding something to the class on the fly:
sub IO::Handle::readline {
my $fh = shift;
if (wantarray) {
return $fh->getlines( );
} else {
return $fh->getline( );
}
}
7.5.4. See Also
The open function in
perlfunc(1) and in Chapter 29 of
Programming Perl; Recipe 7.1; the documentation with the standard
IO::Handle module (also in Chapter 32 of Programming
Perl); and the "Typeglobs and Filehandles" sections of
Chapter 2 of Programming
Perl