Why are non-environment variables passed to the subshell invoqued by command substitution?

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

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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.

Method 2

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.

Method 3

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.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x