-1

It seems command subsitution take the first token as the command, remainings as the arguments of command, not parsed as shell statement. but the bash manual says that :

Bash performs the expansion by executing command in a subshell environment

example

$ c="echo 123 ; echo 124"
$ d=`$c`
$ echo $d
123 ; echo 124

why d is 13 ; echo 124 not 123 124

yuanjianpeng
  • 335
  • 1
  • 9
  • 1
    There are a number of issues -- and this won't end well (or cleanly). *Command substitution* runs a single command or pipeline, but not separate commands. Essentially you are attempting `c='echo "123"; echo "124"'` which contains two commands. You can do `d=$(eval $c)`, result `123 124`, but remember `eval` is only one character away from `evil` -- avoid it. I would refactor your two `echo` statements to avoid any of this mess. – David C. Rankin Aug 27 '21 at 05:13
  • @DavidC.Rankin you don't explain why command execute as a single command, if `c="{ echo 123; echo 124 ; }"` then command subsitution treat it as shell statement – yuanjianpeng Aug 27 '21 at 05:15
  • Don't put a command in a variable. Use a function instead. – Jetchisel Aug 27 '21 at 05:17
  • Or if you really want CommandSub then do it directly `c=$(echo 123 ; echo 124); echo "$c" ` – Jetchisel Aug 27 '21 at 05:21
  • 1
    @yuanjianpeng - from `man 1 bash`, There are two forms: `$(command)` and the older `\`command\``. In neither case is the form `$(command1; command2)`. Bash executes the command in a subshell. It doesn't execute the command and then go looking for another. The command substitution held within a variable doesn't undergo word-splitting when called in `d=$($c)`. As Jetchisel points out -- you can just do the command substitution as `c=$(echo "123"; echo "124")`, you just can't store the inner part in a variable and have it expand in a subsequent command substitution. – David C. Rankin Aug 27 '21 at 05:22
  • Read [BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!](http://mywiki.wooledge.org/BashFAQ/050) – Gordon Davisson Aug 27 '21 at 06:01

1 Answers1

4

This happens because in d=`$c`, the command is not echo 123 ; echo 124 but instead $c.

This means that Bash follows the evaluation steps for so-called simple commands, described in detail in POSIX.

Skipping over irrelevant parts, the steps involved are:

  1. The words that are not variable assignments or redirections shall be expanded. If any fields remain following their expansion, the first field shall be considered the command name and remaining fields are the arguments for the command.

So $c is expanded according to the rules:

Tilde expansion (see Tilde Expansion), parameter expansion (see Parameter Expansion), command substitution (see Command Substitution), and arithmetic expansion (see Arithmetic Expansion) shall be performed, beginning to end. See item 5 in Token Recognition.

$c turns into echo 123 ; echo 124

Field splitting (see Field Splitting) shall be performed on the portions of the fields generated by step 1, unless IFS is null.

echo 123 ; echo 124 turns into echo, 123, ;, echo, 124

If there is a command name, execution shall continue as described in Command Search and Execution .

echo is invoked with the arguments 123, ;, echo, 124, and prints them out.

For it to work the way you expected, there would have to be a step "after expansion, take the resulting string and parse it again according to the shell grammar rules", but there isn't.

Good thing too, because imagine if read -p "Name: " name; would let you enter "; rm -rf /; " and turn echo "Hello $name" into echo "Hello "; rm -rf /; ""

that other guy
  • 116,971
  • 11
  • 170
  • 194