7

According to the Google Shell Style Guide, I should:

Always quote strings containing variables, command substitutions, spaces or shell meta characters, unless careful unquoted expansion is required.

Maybe I am misinterpreting what they mean by "command substitutions", but I am wondering if there is any need to use quotes in the following example:

VAR="$(echo foo bar)"
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
Joshua Spence
  • 732
  • 1
  • 6
  • 19
  • 2
    You don't need quotes in this case. But if unsure, just use quotes! – gniourf_gniourf Jun 20 '16 at 20:52
  • Maybe not in that simple example, but it certainly won't hurt to be consistent. But if `foo` or `bar` was instead `$foo` and `$bar`, then we might want to quote them separately. – Kusalananda Jun 20 '16 at 20:54
  • 4
    In that **specific** case, you don't need outer quotes because assignments suppress string-splitting and glob expansion. – Charles Duffy Jun 20 '16 at 21:02
  • 1
    btw, `VAR` is bad form, except when dealing with variables whose names are defined by your operating system or shell. See fourth paragraph of http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html, keeping in mind that environment variables and shell variables share a namespace – Charles Duffy Jun 20 '16 at 21:03

4 Answers4

12

$(echo foo bar) is indeed a command substitution. In this specific example, you don't need double quotes because a variable assignment creates a “double quote context” for its right-hand side, so VAR=$(…) is equivalent to VAR="$(…)".

In bash, you don't need double quotes in export VAR=$(…) or declare VAR=$(…). But you do need the double quotes in some other sh implementations such as dash.

You do need double quotes in env VAR=$(…) somecommand, in make VAR=$(…), etc. It isn't the equal sign that makes the double quotes optional, it's the fact that the equal sign is parsed by the shell as an assignment.

There are a few other contexts where the double quotes are optional, but you can't go wrong with the simple rule: always use double quotes around variable and command substitutions unless you want the split+glob operator.

Community
  • 1
  • 1
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
  • What if we want the split behaviour, but not glob behaviour? That is, what if we want Bash to split the output string but not glob it? Is this possible? – Utku Nov 01 '17 at 20:09
  • 2
    @Utku You can't exactly say “split but don't glob”, but you can turn off globbing with `set -f` and then use an unquoted substitution. Restore globbing with `set +f` (or save and restore the old setting, if globbing might already have been turned off before). – Gilles 'SO- stop being evil' Nov 01 '17 at 20:33
2

EDIT: (A command substitution is $( ) and the back-tick almost-equivalent, so you're likely interpreting it correctly)

It will certainly not hurt to quote strings in general in shell scripts, unless you're actually relying on globbing etc. to take effect.

In this simple example, the double-quotes are certainly not needed, but for consistency I would probably add them.

If you instead have

VAR=$( echo $foo $bar )

... then I would certainly quote both variables and expression:

VAR="$( echo "$foo" "$bar" )"

especially if any of those variable contained external input or if you knew they had globbing-characters in them.

EDIT: As user @CharlesDuffy points out, the outer double-quotes here are still not needed. I would still add them to be consistent with other variable assignments that do need quoting.

Kusalananda
  • 14,885
  • 3
  • 41
  • 52
2

When not to quote a command substitution?

Answer: When you want the output of the command substitution to be treated as separate arguments.

Example:

We want to extract specific packets (packet numbers 1, 5, 10 and 20) from a .pcap file. The relevant command is the following:

editcap -r capture.pcap select.pcap 1 5 10 20

(Source to the command)

Now, we want to extract random packets from the file. To do that, we will use shuf from GNU Coreutils.

shuf -i 0-50 -n 4
8
24
20
31

The command above has generated 4 random numbers between 0 and 50.

Using it with editcap:

editcap -r source.pcap select.pcap "$(shuf -i 0-50 -n 4)"
editcap: The specified packet number "1
34
4
38" isn't a decimal number

As you can see, quoting the command substitution has resulted in the output string of the shuf to be treated as a single big string, including the newlines and whatnot as well. That is, putting quotes has resulted in the equivalent of the following command:

editcap -r source.pcap select.pcap "1
34
4
38"

Instead, we want Bash the chop the output of shuf and insert this chopped output as separate arguments to editcap. Omitting the quotes accomplishes that:

editcap -r source.pcap select.pcap $(shuf -i 0-50 -n 4)
Utku
  • 2,025
  • 22
  • 42
1

quoting command substitutions realy avoid globbing expansion:

asterisk='*'
echo $(echo $asterisk)
echo $(echo "$asterisk")
echo "$(echo $asterisk)"
echo "$(echo "$asterisk")"
vbem
  • 2,115
  • 2
  • 12
  • 9
  • 1
    You're showing four examples, three of which are wrong. Maybe tell the reader which is correct and why without expecting them to run them all themselves? – Charles Duffy Jun 22 '22 at 17:33