5

Say I have a variable x. When expanding it, should I use $x or "$x"?

(This is intended as a canonical duplicate for any question that revolves around unquoted parameter expansions.)

chepner
  • 497,756
  • 71
  • 530
  • 681
  • 4
    Even though I respect your effort to create a canonical duplicate, I think there is already one for this question. Why don't you use [When to wrap quotes around a shell variable](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable)? – Socowi Mar 06 '19 at 14:53
  • 1
    This is a critical question for shell programming. It comes up often, in many places. By far the best answer that I've seen is the accepted answer to [Security implications of forgetting to quote a variable in bash/POSIX shells](https://unix.stackexchange.com/q/171346/264812). (Don't be put off by the reference to security. The answer is at least as relevant to correctness and reliability as it is to security.) – pjh Mar 14 '19 at 20:04

1 Answers1

10

Always use "$x", at least at first. The cases where this is correct far outnumber the cases where it may be wrong.

Parameter expansions are subject to both word-splitting and pathname expansion, neither of which you usually want. Quoting the expansion preserves the literal content of the parameter.

Compare

$ x="foo * bar"
$ printf '%s\n' "$x"
foo * bar

to

$ printf '%s\n' $x
foo
<every file in the current directory>
bar

If your logic does require word-splitting or pathname expansion to take place, there is a good chance that your script design needs to be changed to avoid that requirement.

Always quoting parameter expansions will, at the very least, reduce the number of bugs you need to fix.


As a corollary, there is never a reason to use ${foo[@]} unquoted. The very existence of @ indexing is to have special behavior (compared to * indexing) when quoted. When unquoted, the two are identical, so you may as well use ${foo[*]}.

The same argument applies to the special parameters $@ and $*.

$ x=("foo bar" baz)
$ printf '%s\n' "${x[@]}"  # expands to two elements words, one per element
foo bar
baz
$ printf '%s\n' "${x[*]}"  # expands to one word; elements join using IFS
foo bar baz
$ printf '%s\n' ${x[*]}    # expands to three words
foo
bar
baz
chepner
  • 497,756
  • 71
  • 530
  • 681
  • The comments concerning `${foo[@]}` can be extended to include `$@` and `$*`. – cdarke Mar 06 '19 at 14:04
  • It might be useful to add some reference links, f/e to [SC2086](https://github.com/koalaman/shellcheck/wiki/SC2086), https://wiki.bash-hackers.org/syntax/expansion/wordsplit, [BashPitfalls #14](http://mywiki.wooledge.org/BashPitfalls#echo_.24foo) – Charles Duffy Mar 06 '19 at 14:13