2

Let's suppose that you have a variable which is subject to word splitting, globing and pattern matching:

var='*
.?'

While I'm pretty sure that everyone agrees that "$var" is the best way to expand the variable as a string literal, I've identified a few cases where you don't need to use the double quotes:

  • Simple assignment: x=$var

  • Case statement: case $var in ...

  • Leftmost part of bash test construct: [[ $var .... ]]

  • UPDATE1: Bash here-string: <<< $var which works starting from bash-4.4 (thank you @GordonDavidson)

  • UPDATE2: Exported assignment (in bash): export x=$var

Is it correct? Is there any other shell/bash statement where the variable isn't subject to glob expansion or word splitting without using double-quotes? where expanding a variable with or without double quotes is 100% equivalent?


The reason why I ask this question is that when reading foreign code, knowing the above mentioned border-cases might help.

For example, one bug that I found in a script that I was debugging is something like:

out_exists="-f a.out"
[[ $out_exists ]] && mv a.out prog.exe
mv: cannot stat ‘a.out’: No such file or directory
Fravadona
  • 13,917
  • 1
  • 23
  • 35
  • 2
    `case` statements and assignments are the only places I intuitively accept that the variable is not subject to field splitting, but even in those cases I would double quote the expression. Finding edge cases in the language to avoid double quoting is folly. Learning the language well enough to understand those edge cases *might* be useful, but expecting others to know the edge cases is probably a bad idea. – William Pursell Mar 12 '22 at 16:17
  • 1
    Possible duplicate of [When to wrap quotes around a shell variable?](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable) – tripleee Mar 12 '22 at 17:18
  • @tripleee I've read it before asking my question; I'm looking for the constructs for which the shell and bash will **always** interpret a variable as a literal string without needing to double-quote it – Fravadona Mar 12 '22 at 17:46
  • 2
    `x=$var` is *not* subject to expansion, however some shells will expand it in e.g. `export x=$var`. BTW, expansion isn't supposed to happen in here-strings (`command <<<$var`), but there's a messy bug in early versions of bash where it sort of half-expands, so you really should double-quote there to avoid trouble. – Gordon Davisson Mar 12 '22 at 18:42
  • Thanks @GordonDavisson, I corrected the edit. So you're saying that according to the spec the variables in herestrings shouldn't need double-quotes? For what version of bash would that be true? – Fravadona Mar 12 '22 at 19:32
  • @Fravadona The here-string bug seems to have been in the original implementation of here-strings (added in [bash v2.05b-alpha1](https://wiki.bash-hackers.org/scripting/bashchanges)), and wasn't fixed until v4.4. Essentially, in `<<<$var`, the variable's value would be word-split and then the words pasted back together with spaces between them (instead of whatever the original whitespace was). See ["Why does `cat <<< $VAR1` lose newlines?"](https://stackoverflow.com/questions/50137589/why-does-cat-var1-lose-newlines) – Gordon Davisson Mar 12 '22 at 21:11
  • 1
    As a rule of thumb, don't rely on implicit implementation. If there is a way to be explicit about not splitting, and not globbing, then be explicit with double quotes. Really the extra precaution to be explicit will just make your code more robust without sacrificing readability. This ROT is one of the few I try to stick with because it has only benefits. It helps readers of you code to be absolutely sure what your intent was and not have to be aware of the specific Bash implementations, variants across versions, bugs... – Léa Gris Mar 12 '22 at 23:27
  • 1
    [Shellcheck](https://www.shellcheck.net/) can identify many cases where quotes are missing. Unfortunately, it seems to not report missing quotes in some cases where they are not required due to simple literal strings being assigned to variables. I *really* wish it wouldn't do that. The stuff that goes into variables tends to change over time. That's what they're for. – pjh Mar 13 '22 at 18:56
  • The example in your edit is a separate FAQ; https://stackoverflow.com/questions/12136948/why-does-shell-ignore-quoting-characters-in-arguments-passed-to-it-through-varia – tripleee Mar 14 '22 at 05:32
  • @tripleee I already refurbished those kind of bad practices in the original code. BTW, using an array wouldn't help `out_exists=( -f a.out ); [[ ${out_exists[@]} ]] && echo a.out`, bash treats it as a test for _"variable non-emptiness"_ instead of _"file presence"_ – Fravadona Mar 14 '22 at 07:45
  • The basic answer to that is don't put code in variables at all. – tripleee Mar 14 '22 at 07:47

2 Answers2

2

This question is a duplicate of What are the contexts where Bash doesn't perform word splitting and globbing?, but that was closed before it was answered.

For a thorough answer to the question see the answer by Stéphane Chazelas to What are the contexts where Bash doesn't perform word splitting and globbing? - Unix & Linux Stack Exchange. Another good answer is in the "Where you can omit the double quotes" section in the answer by Gilles to When is double-quoting necessary? - Unix & Linux Stack Exchange.

There seem to be a small number of cases that aren't covered by the links above:

  • With the for (( expr1 ; expr2 ; expr3 )) ... loop, variable expansions in any of the expressions inside the (( ... )) don't need to be quoted.
  • Several of the expansions described in the Shell Parameter Expansion section of the Bash Reference Manual are described with a word argument that isn't subject to word splitting or pathname expansion (globbing). Examples include ${parameter:-word}, ${parameter#word}, and ${parameter%word}.
pjh
  • 6,388
  • 2
  • 16
  • 17
  • Thanks for the answer and the links; you also made me I realize that I need to rephrase my question: _What are the shell statements where expanding a variable with or without double quotes is 100% equivalent?_ The shell parameter expansions that you mentioned don't pertain to that category. – Fravadona Mar 13 '22 at 08:47
  • @Fravadona, my understanding is that the relevant parameter expansions do meet your condition. For instance, I believe that `${x%"$y"}` is exactly equivalent to `${x%$y}`. – pjh Mar 13 '22 at 18:46
  • 1
    Try this: `x=abc y=?; printf '%s\n' "${x%$y}" "${x%"$y"}"` , you can store a glob pattern in a variable and then use it in the parameter expansion – Fravadona Mar 13 '22 at 19:42
  • 1
    @Fravadona, thanks for the explanation. I was thinking only about pathname expansion and word splitting and forgot about string pattern matching. Duh! It's essentially the same issue as quotes versus no-quotes on the RHS of an `=`, `==` , or `=~` inside `[[ ... ]]`. I suspect there's a lot of code out there that has `${x#$y}` (or similar) where it should be `${x#"$y"}`. Unfortunately, some of it is mine! – pjh Mar 13 '22 at 21:51
1

Great question! If you need to word split a variable, the quotes should be left off.

If I think of other cases, I'll add to this.

var='abc xyz'

set "$var"
echo $1
abc xyz

set $var
echo $1
abc
Cole Tierney
  • 9,571
  • 1
  • 27
  • 35