Bash Shell – Why is a Variable Visible in a Subshell?

bashshellsubshell

The Learning Bash Book mentions that a subshell will inherit only environment variables and file descriptors, etc., and that it will not inherit variables that are not exported:

$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$

As I know the shell will create two subshells for () and for ./file, but why in the () case does the subshell identify the var variable although it is not exported and in the ./file case it did not identify it?

# Strace for () 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631

I tried to use strace to figure out how this happens and surprisingly I found that bash will use the same arguments for the clone system call, so this means that both the forked process in () and ./file should have the same process address space of the parent, so why in the () case is the varible visible to the subshell and the same does not happen for the ./file case, although the same arguments is based on the clone system call?

Best Answer

The Learning Bash Book is wrong. Subshells inherit all variables. Even $$ (the PID of the original shell) is kept. The reason is that for a subshell, the shell just forks and doesn't execute a new shell (on the contrary, when you type ./file, a new command is executed, e.g. a new shell; in the strace output, look at execve and similar). So, basically, it's just a copy (with some documented differences).

Note: this is not specific to bash; this is true for any shell.

Related Question