A most amusing annoyance

Came across this in my travels this morning:

$ perl -E 'sub x { say "y"}; my $x = 'x'; $x->();'
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.


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();
-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.