When you leave a variable expansion unquoted, it undergoes word splitting and filename expansion (i.e. globbing). It isn't parsed as a shell command. In general, when you dynamically construct a shell snippet to execute, the right way to execute it is eval "$a"
where a
contains the string to parse as shell code.
In your first snippet, the value of a
is the string my_cmd --verbose
. Word splitting breaks it into two words my_cmd
and --verbose
. Globbing does nothing since there are no wildcards in any of the words. The command resulting from the expansion of $a
thus consists of two words my_cmd
and --verbose
, so the command my_cmd
(which could be an alias, a function, a builtin or an executable in the PATH) is executed with the single argument --verbose
.
In the second snippet, things are similar, with three words resulting from the expansion: URL=myurl
, my_cmd
and --verbose
. This results in an attempt to execute the command URL=myurl
with two arguments.
The shell command URL=myurl my_cmd --verbose
is parsed differently: the first word is parsed as an assignment to the variable URL
, and since there is a command name after it the assignment sets the environment variable for the duration of the command only. This is part of parsing, not something that's done after expansion, so it requires the equal sign to be part of the shell source code, the equal sign can't be the result of some expansion.
Don't store a command with parameters into a string. Use an array. For a complex command, such as one where you set variables or perform redirection in addition to running the command, use a function if at all possible, and if not use eval
(taking great care of proper quoting).