Bash quote escaping change in 4.4

bashquotingshell

I am updating bash on our embedded platform from 4.1.9 to the latest (4.4.12), and I am seeing a behaviour change in this simple scenario of passing escaped arguments into a script.

Script /tmp/printarg:

#! /bin/sh
echo "ARG |$*|"

And I invoke the script like this:

bash -c "/tmp/printarg \\"abc\\""

I've tried this on several platforms (native x86_64 Linux) running bash 4.3.42, as well as several embedded platforms (ARM and PPC) running bash 4.1.9 and 4.2.37, and all of these platforms report what I would expect:

38$ bash -c "/tmp/printarg \\"abc\\""
ARG |abc|

But, when I run this using bash 4.4.12 (native X86 or embedded platforms), I get this:

$ bash -c "/tmp/printarg \\"abc\\""
ARG |abc\|            <<< trailing backslash

And if I add a space in the command line between the second escaped quote and the ending quote, then I no longer see the extra backslash:

$ bash -c "/tmp/printarg \\"abc\\" "
ARG |abc |            <<< trailing space, but backslash is gone

This feels like a regression. Any thoughts? I also did try enabling the various compat options (compat40, compat41, compat42, compat43) with change.

Best Answer

bash -c "/tmp/printargs \\"abc\\""

Does not escape what you think it does. A backslash-backslash is an escaped backslash, handled by the calling shell — so that is the same as running:

/tmp/printargs \abc\

because the double-quotes are not escaped. You could have just written:

bash -c '/tmp/printargs \abc\'

I'm guessing you actually wanted:

bash -c "/tmp/printargs \"abc\""

which escapes the double quotes, passing a quoted "abc" to the bash -c.

(I'm guessing the different behavior you're seeing is different versions of bash handling the escaped nothing at end of input differently.)

Perl version of printargs (slightly improved behavior):

#!/usr/bin/perl
use feature qw(say);

for (my $i = 0; $i < @ARGV; ++$i) {
        say "$i: |$ARGV[$i]|";
}
Related Question