7

I tend to quote command substitutions as shown below even when assigning their output to a variable:

var="$(command)"

Is that actually needed though? When does it break? The accepted answer here claims:

DIRNAME="$(dirname $FILE)" will not do what you want if $FILE contains whitespace or globbing characters [?*.

The link points to Grey Cat Wiki's great page about quoting but that page doesn't mention quoting command substitutions specifically. And while quoting the variable is clearly needed, quoting the command substitution itself doesn't seem to be.

However, the same post concludes with:

DIRNAME="$(dirname "$FILE")" is the recommended way. You can replace DIRNAME= with a command and a space without changing anything else, and dirname receives the correct string.

Which is what I've always thought as well and have often corrected posts here that didn't quote it. However, the wiki page linked to above also claims that:

There are a few cases where double quotes may be safely omitted:

On the right-hand side of a simple assignment. You may write foo=$bar without quotes. This is POSIX compliant.

[. . . ]

While var=$(command) isn't really a "simple" assignment, I was nevertheless unable to find a case where the quotes were actually necessary:

$ var=$(echo "foo bar baz")  ## whitespace works
$  echo "$var"
foo bar baz

$ var=$(printf "foo\nbar * baz") ## so do globbing characters $ echo "$var" foo bar * baz

$ var1="foo\nbar * baz" $ var=$(printf "$var1") ## printing a variable doesn't make any difference $ echo "$var" foo bar * baz

$ var=$(printf '%s\n' "$var1") $ echo "$var" foo\nbar * baz

$ var=$(printf -- '-e %s\n' "$var1") ## strings starting with - also work $ echo "$var" -e foo\nbar * baz

Of course, the quotes are absolutely necessary if the command substitution is being used directly for things like command1 "$(command2)", but that doesn't seem to be the case when assigning to a variable.

So, what am I missing? Are the quotes ever needed? What corner case will quoting a command substitution when assigning its return value to a variable protect you from? Or is it always OK to not quote a command substitution if it is the right-hand side of a variable assignment operation?

terdon
  • 242,166
  • If you think var=$(command) is not a simple assignment then var=$foo and var=${foo} are also not simple assignments. Only var="foo bar" is really simple. But you can only omit the quotes if the expression on the right side is a shell evaluation like $(...) or ${...} – rudimeier Apr 29 '17 at 15:44
  • @rudimeier yeah, that's what I'm trying to confirm. Obviously "simple" is not a very precise term. As far as I can tell, based on what I show in the question, var=$(command) should indeed be considered "simple" but that's precisely what I'm asking: are there any cases where the quotes would be necessary? – terdon Apr 29 '17 at 15:50
  • @rudimeier I am quoting the relevant bit of that answer in my question: DIRNAME="$(dirname "$FILE")" is the recommended way and that is the claim I am asking about. In any case, that question is about quoting within command substitutions and only mentions quoting the substitution itself in passing, so I felt we could have a better answer specifically dealing with quoting the $(). – terdon Apr 29 '17 at 15:56
  • var="$(command)" is superfluous even if there be white space characters. var=$(command) is absolutely the way to go and there are NO corner cases here. Where quotes are needed are when used as in: var="$(command)" myScript arg1 arg2 ... –  Apr 29 '17 at 16:01
  • 1
    @terdon Herecommends only to avoid bugs if your change that script later: "You can (later) replace DIRNAME= with a command and a space without changing anything". – rudimeier Apr 29 '17 at 16:03
  • 1
    @RakeshSharma: it works without quotes also in var=$(command) myScript arg1 arg2 ... – enzotib Apr 29 '17 at 16:13
  • echo '' | awk -v var=$(echo a b) '{print var}' gives an error unless var is quoted. –  Apr 29 '17 at 16:22
  • 1
    @RakeshSharma: that is not a shell assignment, it is a parameter to the awk command – enzotib Apr 29 '17 at 16:25
  • @Enzotib Ok I was taking to mean that shell is splitting away at whitespace. –  Apr 29 '17 at 16:56

2 Answers2

7

You do not need to quote the expression on the right side of an assignment.

What is irritating you is that the other answer recommends to quote nevertheless. But this is only about code maintenance.

Consider the following correct example:

DIRNAME=$(dirname "$FILE")
echo "debug: dirname is $DIRNAME"
ls "$DIRNAME"

Now after a while using this script you may think that debug message could be removed. So using an editor you will remove the echo line. Then you'll notice that you don't even need the variable DIRNAME anymore and you simply move the ls command to replace the left site of the assignment. Now you may forget to add the needed quotes and you end up with this broken script:

ls $(dirname "$FILE")

The probability for such mistake is even higher if the first author is a shell expert but the second editor is a newbie.

It's of course debatable whether this recommendation to avoid a portable shell feature is really useful. Personally I do it mostly like he recommends. I also do this for the more "simple" assignments like: var="${foo}" (including superfluous curly braces too).

rudimeier
  • 10,315
5

As one reference, Bash's manual is clear on this:

A variable may be assigned to by a statement of the form

name=[value]

If value is not given, the variable is assigned the null string. All values undergo tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, and quote removal (detailed below). [...] Word splitting is not performed, with the exception of "$@" as explained below. Filename expansion is not performed.

No word splitting, no filename expansion, therefore no need for quotes.


As for POSIX, section 2.9.1 Simple Commands:

2. The words that are not variable assignments or redirections shall be expanded. If any fields remain following their expansion If any fields remain following their expansion, the first field shall be considered the command name and remaining fields are the arguments for the command.
[...]
4. Each variable assignment shall be expanded for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal prior to assigning the value.

I'm not sure if that's supposed to be interpreted to mean that field splitting happens only for expansions done at step 2? Step 4 does not mention field splitting, though the section on Field splitting also doesn't mention variable assignments as an exception to producing multiple fields.

ilkkachu
  • 138,973