How to substitute quoted, multi-word strings as arguments?

Solution 1:

Don't use quotes, use an array (see BashFAQ #050):

$ myArgs=("hello" "world" "multiword arg with * ?")
+ myArgs=("hello" "world" "multiword arg with * ?")
$ echo "${myArgs[@]}"
+ echo hello world 'multiword arg with * ?'
hello world multiword arg with * ?

If it really needs to be in the form of quoted strings within a string, you're either going to have to use something like eval "echo $myArg" (which can cause some really nasty bugs, if you aren't careful) or parse it yourself (which is going to be difficult).

Solution 2:

If you want to pass a variable value as a parameter (99% of cases on SO), simply use proper quoting:

arg="foo bar"
command "$arg"

If you want to pass several arguments, use arrays:

args=("foo bar" "baz ban" bay)
command "${args[@]}"

Solution 3:

There is a portable way to split expand a variable but keep spaces. Bash arrays are not needed. Dash (Ubuntu's /bin/sh) would work too.

Use some character to separate arguments that is definitely not used inside the arguments. The below example uses semicolon, but it could be a newline or another character. Change the IFS variable to a newline temporarily when the list of arguments is expanded. Restore IFS to the original value as soon as possible, even if it means doing it in the loop. If the loop is not guaranteed to run at least once, do it after the loop as well.

#! /bin/sh
arg_list='hello world;have a nice day'
save_IFS="$IFS"
IFS=';'
for i in $arg_list; do
  IFS="$save_IFS"
  echo "$i"
done
IFS="$save_IFS"

Note that every expanded argument is printed individually.

$ ./test.sh
hello world
have a nice day