I'm confused over whether bash
variables are exported to subshells and when they are accessible by scripts. My experience so far led me to believe that bash variables are automatically available to subshells. E.g.:
> FOO=bar
> echo $FOO
bar
> (echo $FOO)
bar
The above appears to demonstrate that bash
variables are accessible in subshells.
Given this script:
#! /usr/bin/bash
# c.sh
func()
{
echo before
echo ${FOO}
echo after
}
func
I understand that calling the script in the current shell context gives it access to the current shell's variables:
> . ./c.sh
before
bar
after
If I were to call the script without the "dot space" precedent...
> ./c.sh
before
after
...isn't it the case that the script is called in a subshell? If so, and it's also true that the current shell's variables are available to subshells (as I inferred from the firstmost code-block), why is $FOO
not available to c.sh
when run this way?
Similarly, why is $FOO
also unavailable when c.sh
is run within parentheses - which I understood to mean running the expression in a subshell:
> (./c.sh)
before
after
(If this doesn't muddy this post with too many questions: if "./c.sh
" and "(./c.sh)
" both run the script in a subshell of the current shell, what's the difference between the two ways of calling?)
(...)
runs...
in a separate environment, something most easily achieved (and implemented in bash, dash, and most other POSIX-y shells) using a subshell -- which is to say, a child created byfork()
ing the old shell, but not calling anyexecv
-family function. Thus, the entire in-memory state of the parent is duplicated, including non-exported shell variables../other-script
, by contrast, runsother-script
as a completely separate executable; it does not retain non-exported variables after the child shell (which is not a subshell!) has been invoked. This works as follows:fork()
to create a child. At this point in time, the child still has even non-exported variable state copied../other-script >>log.out
, the child wouldopen("log.out", O_APPEND)
and thenfdup()
the descriptor over to1
, overwriting stdout).execv("./other-script", {"./other-script", NULL})
, instructing the operating system to replace it with a new instance ofother-script
. After this call succeeds, the process running under the child's PID is an entirely new program, and onlyexport
ed variables survive.