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.