A most amusing annoyance

Came across this in my travels this morning:

$ perl -E 'sub x { say "y"}; my $x = 'x'; $x->();'
y
Undefined subroutine &main::1 called at -e line 1.

Why on earth does this compile and run in the first place? Fixing the shell quotes makes the problem go away.

4 Comments

perl -MO=Deparse -E 'sub x { say "y"}; my $x = 'x'; $x->();'                                                                                                             sub x {
    use feature 'current_sub', 'evalbytes', 'fc', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval';
    say 'y';
}
use feature 'current_sub', 'evalbytes', 'fc', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval';
my $x = x();
&$x();
-e syntax OK

Removing the ' from around 'x' also yields the same deparse. Adding spaces like ' x ' breaks with the syntax error you're probably expecting.

My guess is the shell doing something to the quoting based on the lack of spaces.

Using Perl one-liners from a Unix shell requires that you master two programming languages: Perl and your shell. Quoting is a basic feature of shell programming that you must understand if you want to avoid surprises with one-liners.

If your shell code sends corrupted perl source, don't expect perl to guess your mistake.

In most (all?) Unix shells, a parameter can be partly quoted. For example:

echo 'foo'bar'baz'

will print out "foobarbaz". It is this feature that allows parameters like --foo="bar baz" to work, so it's a valuable feature.

Anyway, with your command, perl sees this:

sub x { say "y"}; my $x = x; $x->();

And of course, say returns 1 if it is successful. So the behaviour you see is not really surprising.

thank you that was helpful

Leave a comment

About kd

user-pic Australian perl hacker. Lead author of the Definitive Guide to Catalyst. Dabbles in javascript, social science and statistical analysis. Seems to have been sucked into the world of cloud and devops.