Perl provides 3 ways to get the same file descriptor:
*STDOUT
- typeglob\*STDOUT
is a reference to typeglob*STDOUT{IO}
is either undef or a reference to IO :: Handle
Now I prefer to use \*STDOUT
for consistency. When I use open(my $handle, ...)
to automatically create a file descriptor, Perl gives me a link to typeglob. Therefore, if I want to use either STDOUT
or an open file, I can write
my $handle; if ($path eq "-") { $handle = \*STDOUT; } else { open($handle, '>', $path) or die "Can't open $path: $!"; }
therefore, my descriptor is always a reference to the type glob. This consistency is not too important. The rest of my code does not care if it has a link to typeglob or some other file descriptor.
Given my preference for \*FH
, I was surprised to find an example where *FH
works, but \*FH
fails. In perlsub, "Pass by Reference" , I find this example:
If you plan on creating new file descriptors, you can do this. Pay attention to transfer only naked * FH, not its link.
sub openit { my $path = shift; local *FH; return open (FH, $path) ? *FH : undef; }
I really need to pass *FH
, not \*FH
, although perlsub doesn't explain why. I checked this myself by calling sub in the script:
use strict; use warnings; sub openit { my $path = shift; local *FH; return open (FH, $path) ? *FH : undef; } my $it = openit '</etc/fstab' or die "Can't open it: $!"; if (defined($_ = <$it>)) { print "Its first line is $_"; } else { print "It has no first line\n"; }
When the return value is *FH
, the script works. When I change it to \*FH
, the script fails. Since I use warnings, I see the following:
readline() on unopened filehandle FH at scratch.pl line 11. It has no first line
This happened because \*FH
points to a global variable. In sub, local *FH
given a temporary value for the global type glob. When sub returns, *FH
has lost its temporary value, and FH
returned to the closed file descriptor.
The fix is ββto copy the temporary value. This happens when sub returns *FH
. It copies typeglob because it is not an lvalue sub. Now I know why Sub should return *FH
, not \*FH
.
If I want sub to return the link, I can copy the type glob and then take the link:
local *FH; return open (FH, $path) ? \(my $glob = *FH) : undef;
Or I can auto generate the link:
my $globref; return open ($globref, $path) ? $globref : undef;
If you change the value of STDOUT
and you need to copy the old value, you must copy the type glob using *STDOUT
, not \*STDOUT
. You probably don't need to copy the value.