68

I read here that the purpose of export in a shell is to make the variable available to sub-processes started from the shell.

However, I have also read here and here that "Processes inherit their environment from their parent (the process which started them)."

If this is the case, why do we need export? What am I missing?

Are shell variables not part of the environment by default? What is the difference?

66

Your assumption is that shell variables are in the environment. This is incorrect. The export command is what defines a name to be in the environment at all. Thus:

a=1 b=2
export b

results in the current shell knowing that $a expands to 1 and $b to 2, but subprocesses will not know anything about a because it is not part of the environment (even in the current shell).

Some useful tools:

  • set: Useful for viewing the current shell's parameters, exported-or-not
  • set -k: Sets assigned args in the environment. Consider f() { set -k; env; }; f a=1
  • set -a: Tells the shell to put any name that gets set into the environment. Like putting export before every assignment. Useful for .env files, as in set -a; . .env; set +a.
  • export: Tells the shell to put a name in the environment. Export and assignment are two entirely different operations.
  • env: As an external command, env can only tell you about the inherited environment, thus, it's useful for sanity checking.
  • env -i: Useful for clearing the environment before starting a subprocess.

Alternatives to export:

  1. name=val command # Assignment before command exports that name to the command.
  2. declare/local -x name # Exports name, particularly useful in shell functions when you want to avoid exposing the name to outside scope.
  3. set -a # Exports every following assignment.
  • 3
    set -k is so that one can use cmd ENVVAR=value in place of ENVVAR=value cmd, that won't work in your example unless set -k was run prior to invoking f. Also, not many shells support it nowadays and only for backward compatibility with the Bourne shell. In the Bourne (or Korn) shell, that wouldn't work for functions. And because it affects shell parsing, it has to be in effect at the time the shell reads the code that makes use of it there. – Stéphane Chazelas Nov 7 '17 at 9:49
  • 1
    You may also want to mention set -a – Stéphane Chazelas Nov 7 '17 at 9:50
23

There's a difference between shell variables and environment variables. If you define a shell variable without exporting it, it is not added to the processes environment and thus not inherited to its children.

Using export you tell the shell to add the shell variable to the environment. You can test this using printenv (which just prints its environment to stdout, since it's a child-process you see the effect of exporting variables):

#!/bin/sh

MYVAR="my cool variable"

echo "Without export:"
printenv | grep MYVAR

echo "With export:"
export MYVAR
printenv | grep MYVAR
6

A variable, once exported, is part of the environment. PATH is exported in the shell itself, while custom variables can be exported as needed. Using some setup code:

$ cat subshell.sh 
#!/usr/bin/env bash
declare | grep -e '^PATH=' -e '^foo='

Compare

$ cat test.sh 
#!/usr/bin/env bash
export PATH=/bin
export foo=bar
declare | grep -e '^PATH=' -e '^foo='
./subshell.sh
$ ./test.sh 
PATH=/bin
foo=bar
PATH=/bin
foo=bar

With

$ cat test2.sh 
#!/usr/bin/env bash
PATH=/bin
foo=bar
declare | grep -e '^PATH=' -e '^foo='
./subshell.sh
$ ./test2.sh 
PATH=/bin
foo=bar
PATH=/bin

Since foo is not exported by the shell, and test2.sh never exported it, it was not part of the environment of subshell.sh in the last run.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.