sail0r sail0r - 5 months ago 17
Bash Question

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];'
$ 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


Answer Source

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

As you can check, $_ and $a are unset in the subshell forked for this command 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 $_

which prints an empty line. The reason may be that the _ environment variable just isn't set for a subshell, as it is not invoked but fork/clone-ed, but I don't see this as an adequate explanation.

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.