Search code examples
perlshelldouble-quotessingle-quotes

why does changing from ' to " affect the behavior of this one-liner?


Why is it that simply changing from enclosing my one-liner with ' instead of " affects the behavior of the code? The first line of code produces what is expected and the second line of code gives (to me!) an unexpected result, printing out an unexpected array reference.

$ echo "puke|1|2|3|puke2" | perl -lne 'chomp;@a=split(/\|/,$_);print $a[4];'
puke2
$ echo "puke|1|2|3|puke2" | perl -lne "chomp;@a=split(/\|/,$_);print $a[4];"

This is the Perl version:

$ perl -v

This is perl, v5.10.1 (*) built for x86_64-linux-thread-multi

ARRAY(0x1f79b98)

Solution

  • With double quotes you are letting the shell interpolate variables first.

    As you can check, $_ and $a are unset in the subshell forked for pipe by the parent shell. See a comment on $_ below.

    So the double-quoted version is effectively

    echo "puke|1|2|3|puke2" | perl -lne 'chomp;@a=split(/\|/);print [4];'
    

    what prints the arrayref [4].


    A comment on the effects of having $_ exposed to Bash. Thanks to Borodin for bringing this up.

    The $_ is one of a handful of special shell parameters in Bash. It contains the last argument of the previous command, or the pathname of what invoked the shell or commands (via _ environment variable). See the link for a full description.

    However, here it is being interpreted in a subshell forked to run the perl command, its first. Apparently it is not even set, as seen with

    echo hi;  echo hi | echo $_
    

    which prints an empty line (after first hi). The reason may be that the _ environment variable just isn't set for a subshell for a pipe, but I don't see why this would be the case. For example,

    echo hi; (echo $_)
    

    prints two lines with hi even though ( ) starts a subshell.

    In any case, $_ in the given pipeline isn't set.

    The split part is then split(/\|/), so via default split(/\|/, $_) -- with nothing to split. With -w added this indeed prints a warning for use of uninitialized $_.

    Note that this behavior depends on the shell. The tcsh won't run this with double quotes at all. In ksh and zsh the last part of pipeline runs in the main shell, not a subshell, so $_ is there.