Jaan
Jaan

Reputation: 2350

Quoted parameter expansion with quoted modifier in Bash

I've noticed something weird:

Y=""
echo ${Y:-"\n"}
echo "${Y:-"\n"}"

prints

\n
n

Why is the second line n, not \n? Is this a bug?

It looks as if Bash parsed this as a concatenation of two quoted strings with an unquoted string in between ("${Y:-" and \n and "}") but this doesn't seem to be the case since the commands

echo $(echo "\n")
echo "$(echo "\n")"
echo "${Y:-"'\n'"}"

output

\n
\n
'n'

I'm using GNU bash, version 4.3.11.

Upvotes: 3

Views: 223

Answers (3)

mklement0
mklement0

Reputation: 440546

To complement chepner's helpful answer:

Here's an overview of how the major POSIX-like shells handle the following command:

Y=""
printf '%s\n' ${Y:-"\n"} ${Y:-'\n'} "${Y:-"\n"}" "${Y:-'\n'}"

Note that I've added variations with single quotes.

dash                                    [v0.5.8]
  \n
  \n
  \n
  '\n'

zsh                                     [v5.0.8]
  \n
  \n
  \n
  '\n'

bash                                   [v4.3.42]
  \n
  \n
  n
  '\n'

ksh                                     [93u+]
  \n
  \n
  n
  '\n'
  • Curiously, in all shells, '\n' inside "..." preserves the single quotes, while removing them in the unquoted case.

  • With respect to "\n", both bash and ksh exhibit the oddity uncovered by the OP, while dash and zsh do not.

Upvotes: 2

David C. Rankin
David C. Rankin

Reputation: 84652

Maybe I'm looking at this wrong, but I don't seen any inconsistency in the assignment with the default value Y, quoted or unquoted. The echo expression in each case boils down to:

$ echo "\n"
\n

$ echo ""\n""
n

In the first case you have the quoted string "\n", in the second, you have a bare \n (which is simply n)

Upvotes: 0

chepner
chepner

Reputation: 532418

I suspect there is a bug in the handling of the word following :- (in fact, I seem to recall reading something about this, but I can't recall where).

If the value is not quoted, I get results I would expect...

$ echo ${Y:-\n}
n
$ echo "${Y:-\n}"
\n

This is also the result you get in dash (ignoring the fact that dash actually produces a literal newline since POSIX mandates that echo should process escaped characters, something bash only does if you use the non-standard -e option.)

In this example, quoting the default value preserves the backslash. As the result of the parameter expansion produces the backslash, quote removal does not remove it.

$ echo ${Y:-"\n"}   # Equivalent to echo "\n", so the output makes sense
\n

There doesn't seem to be any reason for bash to behave different in this final example just because the entire parameter expansion is being quoted. It is almost as if quote removal is being applied twice, once to remove the outer double quotes and again to incorrectly remove the backslash.

# Quote removal discards the backslash: OK
$ echo \n
n
# Quote removal discards the double quotes: OK
$ echo "n"
n
# Quote removal discards the first backslash after `\\` is recognized
# as a quoted backslash: OK
$ echo \\n
\n 
# Quote removal discards the double quotes, but leaves
# backslash: OK
$ echo "\n"
\n
# Is quote removal discarding both the double quotes *and* the backslash? Not OK
$ echo "${Y:-"\n"}"
n

Related, zsh (with the bsd_echo) option set outputs \n, not n.

% Y=""
% echo "${Y:-"\n"}"
\n

Upvotes: 3

Related Questions