Search code examples
bashcommand-substitution

bash command substitution executed as a single command not parsed as shell statement


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


Solution

  • 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 /; ""