Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I say print $somehash{$var}{fh} "foo"?

Tags:

perl

I have a line of code along the lines of:

print $somehash{$var}{fh} "foo";

The hash contains the filehandle a few levels down. The error is:

String found where operator expected at test.pl line 10, near "} "foo""

I can fix it by doing this:

my $fh = $somehash{$var}{fh};
print $fh "foo";

...but is there a one-liner?

like image 264
mike Avatar asked Feb 12 '09 23:02

mike


3 Answers

see http://perldoc.perl.org/functions/print.html

Note that if you're storing FILEHANDLEs in an array, or if you're using any other expression more complex than a scalar variable to retrieve it, you will have to use a block returning the filehandle value instead: ...

So, in your case, you would use a block like this:

print { $somehash{$var}{fh} } "foo";
like image 147
j w Avatar answered Nov 07 '22 11:11

j w


If you have anything other than a simple scalar as your filehandle, you need to wrap the reference holding the filehandle in braces so Perl knows how to parse the statement:

print { $somehash{$var}{fh} } $foo;

Part of Perl Best Practices says to always wrap filehandles in braces just for this reason, although I don't get that nutty with it.

The syntax is odd because print is an indirect method on a filehandle object:

method_name Object @arguments;

You might have seen this in old-school CGI.pm. Here are two indirect method calls:

use CGI;

my $cgi_object = new CGI 'cat=Buster&bird=nightengale';

my $value = param $cgi_object 'bird';

print "Indirect value is $value\n";

That almost works fine (see Schwern's answer about the ambiguity) as long as the object is in a simple scalar. However, if I put the $cgi_object in a hash, I get the same syntax error you got with print. I can put the braces around the hash access to make it work out. Continuing with the previous code:

my %hash;

$hash{animals}{cgi} = $cgi_object;

# $value = param $hash{animals}{cgi} 'cat';  # syntax error
$value = param { $hash{animals}{cgi} } 'cat';
print "Braced value is $value\n";

That's all a bit clunky so just use the arrow notation for everything instead:

my $cgi_object = CGI->new( ... );
$cgi_object->param( ... );

$hash{animals}{cgi}->param( ... );

You can do the same with filehandles, although you have to use the IO::Handle module to make it all work out:

use IO::Handle;

STDOUT->print( 'Hello World' );

open my( $fh ), ">", $filename or die ...;
$fh->print( ... );

$hash{animals}{fh} = $fh;

$hash{animals}{fh}->print( ... );
like image 40
brian d foy Avatar answered Nov 07 '22 11:11

brian d foy


The above answers are all correct. The reason they don't allow a full expression in there is print FH LIST is already pretty weird syntax. To put anything more complicated in there would introduce a ton of ambiguous syntax. The block removed that ambiguity.

To see where this madness leads to, consider the horror that is indirect object syntax.

foo $bar;  # Is that foo($bar) or $bar->foo()?  Good luck!
like image 22
Schwern Avatar answered Nov 07 '22 11:11

Schwern