0

Bash manual says:

Command substitution, commands grouped with parentheses, and asynchronous commands are invoked in a subshell environment that is a duplicate of the shell environment, except that traps caught by the shell are reset to the values that the shell inherited from its parent at invocation.

In this example,b isn't an environment variable, so b doesn't exist in the subshell created by command substitution. Then why is c assigned the value of b by command substituion? Is it because the parameter expansion happens for $b in the shell process before creating a subshell to execute echo 1?

$ b=1
$ c=$(echo $b)
$ echo $c
1
6

No, the subshell was created first.

A shell execution environment contains shell parameters set by variable assignments and environment variables. A subshell environment was created by duplicating the shell environment, so it contains all the variables of the current shell environment.

See the example:

$ b=1
$ c=$(b=2; echo "$b")
$ echo "$c"
2

The output is 2 instead of 1.


A subshell environment created by command substitution is different with a shell environment created by calling the shell executable.

When you call the shell as:

$ bash -c :

the the current shell used execve() to create new shell process, something like:

execve("/bin/bash", ["bash", "-c", ":"], [/* 64 vars */]) = 0

the last argument passed to execve contains all the environment variables.

That's why you need to export the variables to push it to the environment variables, which will be included in subsequently executed commands:

$ a=; export a
$ strace -e execve bash -c :
execve("/bin/bash", ["bash", "-c", ":"], [/* 65 vars */]) = 0
+++ exited with 0 +++

Notice the environment variables change from 64 to 65. And variables which are not exported will not be passed to new shell environment:

$ a=; b=; export a
$ strace -e execve bash -c :
execve("/bin/bash", ["bash", "-c", ":"], [/* 65 vars */]) = 0
+++ exited with 0 +++

Notice the environment variables are still 65.


In command substitution, the shell used fork() to create new shell process, which just copied the current shell environment - which contains both variables set and environment variables.

  • rui@ruir:~$ b=1 rui@ruir:~$ bash rui@ruir:~$ echo $b (nothing) – Rui F Ribeiro Feb 11 '16 at 2:34
  • 1
    @RuiFRibeiro: You were confused between child process environment and subshell environment. – cuonglm Feb 11 '16 at 2:35
  • thanks. In my example,b isn't an environment variable, why is c assigned the value of b by command substituion? – Tim Feb 11 '16 at 2:49
  • I took the liberty of editing the English, and made a word change that I think will help @Tim understand better the issue of his last question. – Rui F Ribeiro Feb 11 '16 at 3:00
  • @Rui: Does the reply answer my question in my last comment, which is also in my post? I.e. In my example, b isn't an environment variable, so b doesn't exist in the subshell, why is c assigned the value of b by command substituion? – Tim Feb 11 '16 at 3:20
3

Yes b isn't an environment variable.
But: Yes, b does exist in the subshell created by command substitution:

$ b=11; c="$(echo $b)"; echo "$c"          ### b exists in subshell.
11
$ b=11; c="$(b=33; echo $b)"; echo "$c"    ### $b is not replaced before
33                                         ### the subshell is executed.

What does not receive the variables is a "full child process":

$ b=11; bash -c 'echo "<$b>"'              ### b does not exist.
<>
$ b=11 bash -c 'echo "<$b>"'               ### environment b.
<11> 

Except that a process may receive variables in the environment, of course.


Last line at Wooledge SubShell:

In the subshell, the regular shell variable a is visible; but because it is not exported, the full child process does not see it.

2

Command substitution causes a normal call to the shell interpreter and this interpreter will run the echo command in a sub-process.

The sub-process is needed as the shell needs to establish a pipe to the echo command in order to be able to read the results.

The sub-process that runs the echo command is created by fork() and this creates copies of all variables from the main shell process. This is why $b is accessible by the echo command.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.