In case the sub-shell runs in the initial script:
(command1; command2; ...)
a sub-shell is a direct copy of the original shell created by fork() , and therefore has direct access to its own copy of all the source variables available to it.
Suppose that the commands ( command1 , command2 , etc.) in the sub-shell are themselves shells. These commands are executed by the sub-shell that calls fork() and then exec() to create a new shell, and the new shell does not inherit non-exported variables from the original shell.
Direct link to your examples:
$ a=100 $ (echo $a) 100 $
Here, the sub-shell has its own copy of all variables (in particular, a ), to which the parent shell had access. Any changes made to the sub-shell will not be reflected in the parent shell, of course, like this:
$ a=100 $ (echo $a; a=200; echo $a) 100 200 $ echo $a 100 $
Now your second example:
$ cat b.sh echo $a $ a=100 $ ./b.sh $ . ./b.sh 100 $ source ./b.sh 100 $ a=200 ./b.sh 200 $ echo $a 100 $ export a $ ./b.sh 100 $
The variable a not exported, so when b.sh first launched b.sh it does not matter for $a , so it has something in common with an empty string. The second two examples are cheat; the shell reads b.sh script as if it were part of the current shell (no fork() ), so the variables are still available to b.sh , therefore, each time it echoes 100. (Dot or . is an older mechanism for reading script in the current shell that uses the Bourne shell in UNIX 7. The source command is borrowed from the C shells as an equivalent mechanism.)
The a=200 ./b.sh exports a throughout the command, so b.sh sees and drives off the changed value of 200 , but the main shell has a unchanged. Then, when a exported, it is available to b.sh automatically, so it sees and echoes the last 100.