18

I'm running shellcheck on my scripts and often get this warning (which in this case is correct, because cd foo bar baz makes no sense):

cd ${SOME_DIR} || exit 1
   ^-- SC2046: Quote this to prevent word splitting.

This warning is mostly a good one. One exception when the variable contains multiple arguments:

gcc ${OPTIONS} ...
    ^-- SC2046: Quote this to prevent word splitting.

Is there a convention for being more explicit about intentional word splitting, possibly avoiding this shellcheck warning?

ulidtko
  • 14,740
  • 10
  • 56
  • 88
Andreas
  • 5,086
  • 3
  • 16
  • 36
  • @KamilCuk updated. Did not know of local suppression since it didn't show up in the man pages. Good to know :) – Andreas Jun 29 '20 at 12:07
  • @GillesQuenot updated with different example. – Andreas Jun 29 '20 at 12:08
  • @Andreas : At least the first case still does not make sense to me, since word splitting might produce something like `cd foo bar baz`, which is wrong anyway. The second example is a bit unfortunate, because in conjunction with the `echo` command would simply express "replace a sequence of spaces in the variable by a single space in the output". A more realistic example would be something like `gcc $OPTIONS foo.cc`, where you want the _OPTIONS_ string to be split on white space, or cases where you fiddle with `IFS` for splitting on other characters. – user1934428 Jun 29 '20 at 12:12
  • Also after the update not quoting `DIR_STRING` is not a good idea. Regarding quoting assignments: the right-hand side [does not undergo word splitting](https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameters), so `var=$(echo 'foo   bar')` will preserve the spaces (but notice inner quoting!), but `var=foo   bar` still needs quotes, `var='foo   bar'`. – Benjamin W. Jun 29 '20 at 12:13
  • @KamilCuk : IMO you should make your comment an answer, since it not only answers the question of the OP, but looks to be very helpful in general. I wonder, if there is also a corresponding `enable=` directive, because one likely wants to turn off the warnings only for a small section of the code. – user1934428 Jun 29 '20 at 12:14
  • And to clarify, within the command substitution word splitting *does* happen, just no on the right-hand side as a whole before assigning. – Benjamin W. Jun 29 '20 at 12:15
  • You still haven't given an example of why you would *want* word-splitting. Virtually every example I can think of is covered by the use of an array instead of an unquoted parameter expansion. – chepner Jun 29 '20 at 12:16
  • @user1934428 "Which is mostly a valid warning" is a reference to the first example being a valid warning, because as you say `cd foo bar baz` is incorrect. gcc ${OPTIONS} is a good example, maybe should use that one instead (despite it not being in the code I'm having issues with right now). – Andreas Jun 29 '20 at 12:18
  • @chepner `gcc ${OPTIONS}` – Andreas Jun 29 '20 at 12:19
  • @Andreas `gcc "${OPTIONS[@]}"`. `OPTIONS` should be an array, not a space-separated string. – chepner Jun 29 '20 at 12:24
  • `gcc ${OPTIONS} is a good example` Yes, it's a great example why you should use bash arrays and quote the expansions. – KamilCuk Jun 29 '20 at 12:25
  • @KamilCuk : Correct. But provided the programmer **knows** what he is doing (i.e. not have options with embedded spaces), I could imagine that he is using the approach of the OP, but of course documenting in a comment, why is going this track. Perhaps the author of the script has set up the options string himself and knows that it will always be safe. Also note that the question is tagged _sh_ too, so maybe the OP does not have options at his disposal. – user1934428 Jun 29 '20 at 12:39

4 Answers4

23

Simply add double quotes when no split is the intent:

cd "${SOME_DIR}" || exit 1

Perform explicit splitting into an array:

read -ra gcc_options <<<"${OPTIONS}"
gcc "${gcc_options[@]}"

Or disable shellcheck for the next statement, indicating you reviewed the operation as conform to the intent:

# shellcheck disable=SC2046 # Intended splitting of OPTIONS
gcc ${OPTIONS}

Sometimes Reading The Fine Manual is a better option than asking here:

Shellcheck gives links to its Wiki for code inspection warnings. The SC2046 Quote this to prevent word splitting wiki entry already mentions the use of read -a in Bash and how to disable this code inspection for specific cases with non-Bash shell grammars.

Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • Maybe we should also point out that your first approach would require bash or ksh. Since the OP also used the _sh_ tag, he is possibly also interested in a Bourne/POSIX shell solution. – user1934428 Jun 29 '20 at 12:47
  • 2
    Shouldn't the second one be `read -ra gcc_options <<< "$OPTIONS"`? With `mapfile`, you need the options on separate lines. – Benjamin W. Jun 29 '20 at 12:57
  • While the `read -ra` may look a little clunky compared to shellcheck disable, it is "required" when I have a command with arguments of which some should be split and others not. For example, I might want to split OPTIONS, but not OUTFILE. This answer covers all cases. – Andreas Jun 29 '20 at 13:45
  • 1
    The wiki link points to SC20**4**6, not [SC20**8**6](https://github.com/koalaman/shellcheck/wiki/Sc2086). – Cnly Dec 07 '21 at 04:15
  • @Cnly fixed. When you have enough reputation, you can propose edits on answers as long as it respects its original intent. Thank you for spotting this error. The link was right to SC2046, but the mention of SC2086 was wrong actually. – Léa Gris Dec 07 '21 at 06:51
3

Apparently, shellcheck does not complain about the missing quotes for variables (SC2046 or SC2086), if typing unquoted ${ARGS} using parameter expansion format:

${ARGS:+ $ARGS}

(The space after :+ is for readability).

Noam Manos
  • 15,216
  • 3
  • 86
  • 85
2

In your script, comments of the form # shellcheck disable=... will disable a particular warning.

options="a b c"
# shellcheck disable=2086
foo $options

If you are running the shellcheck script locally, you can use the -e option instead of adding directives to the script.

$ cat tmp.sh
#/bin/sh

options="a b c"

foo $options
$ shellcheck tmp.sh

In tmp.sh line 5:
foo $options
    ^------^ SC2086: Double quote to prevent globbing and word splitting.

Did you mean:
foo "$options"

For more information:
  https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...
$ spellcheck -e SC2086 foo.sh
$
chepner
  • 497,756
  • 71
  • 530
  • 681
-1

In any cases you showed, there is no reason not to quote expansions. Use quotes.

Is there a convention for being more explicit about intentional word splitting, possibly avoiding this shellcheck warning?

The convention would be to perform word splitting with mapfile or read -a.

If you really want to use word splitting, then a convention would be to add a comment explaining the reason why your code wants to depend on word splitting and then additionally you could add a warning:

# I use word splitting here, because...
# shellcheck disable=SC2046

to disable the shellcheck warning, see shellcheck/wiki/ignore.

Note: Use lower case variables in your scripts. By convention, upper case variables are used for exported variables, like PATH PWD UID COLUMNS LINES etc.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111