After some reading I will try to answer more particularly.
Why does command substitution behave in such way?
$ a='echo x; echo y'
$ echo $($a) # expect 'x y'
Lets notice that the substitution of variable
$a is performing in the subshell produced during command substitution, not in the current shell (1,2,3). Hence the subshell executes the command
echo x; echo y (value of variable
a is inherited)
So now we only need to understand why does shell behave in such way:
$ a='echo x; echo y'
x; echo y
According to [Bash Reference Manual]:
… There are seven kinds of expansion … The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and filename expansion …
— from the section 3.5 Shell Expansions
… The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.
The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words using these characters as field terminators …
— from the section 3.5.7 Word Splitting
(The default value of
… The IFS variable is used to split only the results of expansion, not all words (see Word Splitting) …
— from the Appendix B Major Differences From The Bourne Shell
A character that, when unquoted, separates words. A metacharacter is a space, tab, newline, or one of the following characters:
— from the section 2. Definitions
The brief description of the shell’s operation when it reads and executes a command. Basically, the shell does the following:
- Reads its input.
- Breaks the input into words and operators, obeying the quoting rules. These tokens are separated by metacharacters.
- Parses the tokens into simple and compound commands.
- Performs the various shell expansions.
- Performs any necessary redirections.
- Executes the command
— from the section 3.1.1 Shell Operation
So now we can see that there are two different kinds of "word splitting" — the "inital" word splitting (step 2) and word splitting that is kind of shell expansions (step 4).
The "inital" word splitting (step 2) respects metacharacters like
). The outcome of "inital" word splitting is parsed (step 3) and all its metacharacters and keywords are recognized.
Subsequently bash performs the various shell expansions (step 4) and, among others, word splitting (as one kind of expansions). "This" word splitting treats only characters of
IFS as delimiters. "This" word splitting does not respect metacharacters. Outcome of "this" word splitting is not parsed again as well as outcome of any expansion. Thus metacharecters and keywords in the result of expansions are not recognized.
; within value of variable
a is not treated as metacharacter.
'echo' 'x;' 'echo' 'y'. Even if value of
'echo x ; echo y' the word
';' would not be treated as metacharacter and command
$a would become
'echo' 'x' ';' 'echo' 'y'.
Metacharacters and keywords (
while etc.) cannot be a result of expansions.
But program names (as well as builtin commands, functions and aliases) can . It is a source of confusion, because it allows to put simple commands in variables and doesn't allow to put compound ones.
$ 'echo' 'a' ';' 'echo' 'b' # ';' is a literal
a ; echo b
$ "$cmd" a ; $(printf echo) b # ';' is a metacharacter
$ cmd='echo a' # simple command is executed properly
$ cmd='echo a;' # but metacharacters are not treated
$ $cmd echo b
a; echo b
$ echo a $(echo ';') echo b # ';' is a literal
a ; echo b
$ $(printf if) true; then echo a; fi # error while parsing
bash: syntax error near unexpected token 'then'
$ $(printf if) true; $(printf then) echo a; $(printf fi) # command not found
bash: if: command not found
bash: then: command not found
bash: fi: command not found
How to perform the
command substitution for list of commands stored in a variable without using eval and bash -c (since eval is evil)?
Probalby, the right answer is "this is evil too" so we don't need to avoid eval :)
Variables hold data. Functions hold code. Don't put code inside variables!..
— from the article I'm trying to put a command in a variable, but the complex cases always fail!
Bash shell command substitution
Why is a variable visible in a subshell?
Do parentheses really put the command in a subshell?
Bash Reference Manual
I'm trying to put a command in a variable, but the complex cases always fail!
conversation in debian bug tracking system