bash v3.2 (though I think holds for newer versions too):

In section 3.7.4 Environment, the docs say:

On invocation, the shell scans its own environment and creates a parameter for each name found, automatically marking it for export to child processes.

And later in Appendix B Major Differences From The Bourne Shell, the docs say:

Variables present in the shell’s initial environment are automatically exported to child processes. The Bourne shell does not normally do this unless the variables are explicitly marked using the export command.

I don't understand what this means.

In the following, cmd1.sh comprises


echo yes $ben from cmd1

And cmd2.sh comprises


echo yes $ben from cmd2

I first understood the docs to mean that all assigned variables will be exported (ie there was no need to export variables), ie when running


I expected this to print

yes you from cmd1
yes you from cmd2

But instead it prints

yes from cmd1
yes from cmd2

So variable ben doesn't appear to be automatically exported. Then I thought the docs might instead mean that all environment variables will be exported, ie when running

export ben;

Because cmd1 receives an environment variable of ben, then ben will be automatically exported such that it will be visible in cmd2. Ie I expected this to print the following (and indeed the following is printed):

yes you from cmd1
yes you from cmd2

However, to test whether this is different from Bourne shell (as the docs claimed) I ran the exact same commands, but changing the shebang to point to /bin/sh instead of /bin/bash, and I obtained the exact same result. Ie I did not see any difference. In Bourne shell, I was expecting to see an output of something like

yes you from cmd1
yes from cmd2

Can anyone help me to understand what the docs are referring to when they talk of "automatically" marking parameters for export, and how this is different to Bourne shell?

Nb I did spot this question regarding a specific difference between export behaviour in bash and bourne, but that doesn't seem to be relevant.

  • Check your console. I think, you use dash. (Check : ls -la /bin/sh)
    – K-attila-
    Commented Mar 28, 2023 at 8:05
  • It is very, very unlikely that your /bin/sh is the Bourne shell. What operating system are you using? The Bourne shell is not really seen in the wild anymore.
    – terdon
    Commented Mar 28, 2023 at 8:38
  • Ah I see! I'm not sure which shell is being used... @K-att- running ls -la /bin/sh gave -rwxr-xr-x 1 root wheel 150384 24 Aug 2022 /bin/sh. I'm running macOS Monterey, version 12.6. Ie a modern Mac...
    – Ben Ldr
    Commented Mar 28, 2023 at 16:30
  • @BenLdr, it's that Bash 3.2, try /bin/sh --version. Just note that when called as sh, Bash goes to its POSIX mode, where it acts slightly different from when called as bash...
    – ilkkachu
    Commented Mar 28, 2023 at 18:14
  • @ilkkachu you're absolutely right. /bin/sh --version shows it's Bash 3.2.
    – Ben Ldr
    Commented Mar 29, 2023 at 6:35

3 Answers 3


"Variables present in the shell’s initial environment are automatically exported to child processes. The Bourne shell does not normally do this unless the variables are explicitly marked using the export command.".


% export FOO=abc
% bash -c 'FOO=xyz; echo "bash: FOO=$FOO"; echo "env:"; env |grep FOO'
bash: FOO=xyz

Here, I've set FOO in my interactive shell (zsh, but it doesn't matter), and set it as exported. Then I run Bash, which receives that variable in its environment, changes its value, prints it, and then runs env. That's an external command, so it only sees variables that inner Bash explicitly passes to it. We see the modified value of FOO is visible in the inner Bash, and in env, so Bash indeed imported the variable from its environment, and passed it on as it would pass any exported variable.

The other behaviour the quote seems to describe would be that the inner shell would not pass the variable on to env, and you'd see something like this instead:

bash: FOO=xyz

I don't know what the actual behaviour with all historical implementations has been, all I could reproduce was this with heirloom-sh (the same behavior as Kusalananda mentioned), only the original value of the variable is passed on:

% ./heirloom-sh -c 'FOO=xyz; echo "sh: FOO=$FOO"; echo "env:"; env |grep FOO'
sh: FOO=xyz

An explicit export FOO inner shell would have the current value also passed on. Here, the shell does make the original value of FOO visible to the script, so echo "FOO=$FOO" as the first thing would also print FOO=abc.


You don't use an original Bourne shell, so you would not expect to see the Bourne shell behaviour. The POSIX sh shell (implemented by either bash or dash or some other shell running in POSIX mode on your system) and bash behaves the same, and does not have the Bourne shell behaviour with regards to environment variables.

In a POSIX shell, like sh, bash, dash and most other common shells on Unix nowadays, environment variables that are inherited by the shell from the shell's parent process are passed on to any child processes that the shell starts. The text in the Appendix refers to this as the environment variables being "automatically exported to child processes".

The Appendix claims that this was not the case in the original Bourne shell, and that environment variables to be inherited by child processes had to be explicitly exported with the export command, no matter if they were inherited from the parent environment by the shell or created within the shell.

The shell that I know of that comes closest to being an original Bourne shell is /usr/sunos/bin/sh on Solaris. This is the actual original Bourne shell in terms of code base, but it may have had a few updates throughout the years.

According to its manual, this shell does not behave quite like what the bash documentation's Appendix describes. It does pass on inherited environment variables to its child processes. But if you want to modify the inherited environment variables, you would have to export them for child processes to see changed values:

[...] On invocation, the shell scans the environment and creates a parameter for each name found, giving it the corresponding value. If the user modifies the value of any of these parameters or creates new parameters, none of these affects the environment unless the export command is used to bind the shell's parameter to the environment (see also set –a). A parameter can be removed from the environment with the unset command. The environment seen by any executed command is thus composed of any unmodified name-value pairs originally inherited by the shell, minus any pairs removed by unset, plus any modifications or additions, all of which must be noted in export commands.

  • That fairly much matches my recollection of at least some pre-POSIX versions of /bin/sh. Commented Mar 5 at 12:43

Un-exported variables aren't in the environment for child processes. Which means that when you run a child shell, they aren't in that shell's initial environment.

"Variables present in the shell’s initial environment are automatically exported to child processes" is true, but if the variables aren't in the environment already, they're not going to available to that shell or be re-exported to the child shell's children.

That's pretty much the distinction between an un-exported shell variable and an exported environment variable. Shell variables are local to the current shell, exported variables are visible to the current shell (or other program) and its children.

Also worth noting: child processes can not change the environment of their parent...so changing an env var in a child has no effect on the parent.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .