Bash – Why do options in a quoted variable fail, but work when unquoted

bashoptionsquotingshellvariable

I read about that I should quote variables in bash, e.g. "$foo" instead of $foo. However, while writing a script, I found an a case where it works without quotes but not with them:

wget_options='--mirror --no-host-directories'
local_root="$1" # ./testdir recieved from command line
remote_root="$2" # ftp://XXX recieved from command line 
relative_path="$3" # /XXX received from command line

This one works:

wget $wget_options --directory_prefix="$local_root" "$remote_root$relative_path"

This one does not (note the double quotes aroung $wget_options):

wget "$wget_options" --directory_prefix="$local_root" "$remote_root$relative_path"
  • What is the reason for this?

  • Is the first line the good version; or should I suspect that there is
    a hidden error somewhere that causes this behavior?

  • In general, where do I find good documentation to understand how bash and its quoting works? During writing this script I feel that I started to work on a trial-and-error base instead of understanding the rules.

Best Answer

Basically, you should double quote variable expansions to protect them from word splitting (and filename generation). However, in your example,

wget_options='--mirror --no-host-directories'
wget $wget_options --directory_prefix="$local_root" "$remote_root$relative_path"

word splitting is exactly what you want.

With "$wget_options" (quoted), wget doesn't know what to do with the single argument --mirror --no-host-directories and complains

wget: unknown option -- mirror --no-host-directories

For wget to see the two options --mirror and --no-host-directories as separate, word splitting has to occur.

There are more robust ways of doing this. If you are using bash or any other shell that uses arrays like bash do, see glenn jackman's answer. Gilles' answer additionally describes an alternative solution for plainer shells such as the standard /bin/sh. Both essentially store each option as a separate element in an array.

Related question with good answers: Why does my shell script choke on whitespace or other special characters?


Double quoting variable expansions is a good rule of thumb. Do that. Then be aware of the very few cases where you shouldn't do that. These will present themselves to you through diagnostic messages, such as the above error message.

There are also a few cases where you don't need to quote variable expansions. But it's easier to continue using double quotes anyway as it doesn't make much difference. One such case is

variable=$other_variable

Another one is

case $variable in
    ...) ... ;;
esac