[linux] Defining a variable with or without export

What is export for?

What is the difference between:

export name=value

and

name=value

This question is related to linux bash shell

The answer is


export makes the variable available to sub-processes.

That is,

export name=value

means that the variable name is available to any process you run from that shell process. If you want a process to make use of this variable, use export, and run the process from that shell.

name=value

means the variable scope is restricted to the shell, and is not available to any other process. You would use this for (say) loop variables, temporary variables etc.

It's important to note that exporting a variable doesn't make it available to parent processes. That is, specifying and exporting a variable in a spawned process doesn't make it available in the process that launched it.


As you might already know, UNIX allows processes to have a set of environment variables, which are key/value pairs, both key and value being strings. Operating system is responsible for keeping these pairs for each process separately.

Program can access its environment variables through this UNIX API:

  • char *getenv(const char *name);
  • int setenv(const char *name, const char *value, int override);
  • int unsetenv(const char *name);

Processes also inherit environment variables from parent processes. Operating system is responsible for creating a copy of all "envars" at the moment the child process is created.

Bash, among other shells, is capable of setting its environment variables on user request. This is what export exists for.

export is a Bash command to set environment variable for Bash. All variables set with this command would be inherited by all processes that this Bash would create.

More on Environment in Bash

Another kind of variable in Bash is internal variable. Since Bash is not just interactive shell, it is in fact a script interpreter, as any other interpreter (e.g. Python) it is capable of keeping its own set of variables. It should be mentioned that Bash (unlike Python) supports only string variables.

Notation for defining Bash variables is name=value. These variables stay inside Bash and have nothing to do with environment variables kept by operating system.

More on Shell Parameters (including variables)

Also worth noting that, according to Bash reference manual:

The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments, as described in Shell Parameters. These assignment statements affect only the environment seen by that command.


To sum things up:

  • export is used to set environment variable in operating system. This variable will be available to all child processes created by current Bash process ever after.
  • Bash variable notation (name=value) is used to set local variables available only to current process of bash
  • Bash variable notation prefixing another command creates environment variable only for scope of that command.

Here's yet another example:

VARTEST="value of VARTEST" 
#export VARTEST="value of VARTEST" 
sudo env | grep -i vartest 
sudo echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}" 
sudo bash -c 'echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}"'  

Only by using export VARTEST the value of VARTEST is available in sudo bash -c '...'!

For further examples see:


export will make the variable available to all shells forked from the current shell.


To illustrate what the other answers are saying:

$ foo="Hello, World"
$ echo $foo
Hello, World
$ bar="Goodbye"
$ export foo
$ bash
bash-3.2$ echo $foo
Hello, World
bash-3.2$ echo $bar

bash-3.2$ 

Others have answered that export makes the variable available to subshells, and that is correct but merely a side effect. When you export a variable, it puts that variable in the environment of the current shell (ie the shell calls putenv(3) or setenv(3)).
The environment of a process is inherited across exec, making the variable visible in subshells.

Edit (with 5 year's perspective): this is a silly answer. The purpose of 'export' is to make variables "be in the environment of subsequently executed commands", whether those commands be subshells or subprocesses. A naive implementation would be to simply put the variable in the environment of the shell, but this would make it impossible to implement export -p.


Two of the creators of UNIX, Brian Kernighan and Rob Pike, explain this in their book "The UNIX Programming Environment". Google for the title and you'll easily find a pdf version.

They address shell variables in section 3.6, and focus on the use of the export command at the end of that section:

When you want to make the value of a variable accessible in sub-shells, the shell's export command should be used. (You might think about why there is no way to export the value of a variable from a sub-shell to its parent).


It has been said that it's not necessary to export in bash when spawning subshells, while others said the exact opposite. It is important to note the difference between subshells (those that are created by (), ``, $() or loops) and subprocesses (processes that are invoked by name, for example a literal bash appearing in your script).

  • Subshells will have access to all variables from the parent, regardless of their exported state.
  • Subprocesses will only see the exported variables.

What is common in these two constructs is that neither can pass variables back to the parent shell.

$ noexport=noexport; export export=export; (echo subshell: $noexport $export; subshell=subshell); bash -c 'echo subprocess: $noexport $export; subprocess=subprocess'; echo parent: $subshell $subprocess
subshell: noexport export
subprocess: export
parent:

There is one more source of confusion: some think that 'forked' subprocesses are the ones that don't see non-exported variables. Usually fork()s are immediately followed by exec()s, and that's why it would seem that the fork() is the thing to look for, while in fact it's the exec(). You can run commands without fork()ing first with the exec command, and processes started by this method will also have no access to unexported variables:

$ noexport=noexport; export export=export; exec bash -c 'echo execd process: $noexport $export; execd=execd'; echo parent: $execd
execd process: export

Note that we don't see the parent: line this time, because we have replaced the parent shell with the exec command, so there's nothing left to execute that command.


export NAME=value for settings and variables that have meaning to a subprocess.

NAME=value for temporary or loop variables private to the current shell process.

In more detail, export marks the variable name in the environment that copies to a subprocesses and their subprocesses upon creation. No name or value is ever copied back from the subprocess.

  • A common error is to place a space around the equal sign:

    $ export FOO = "bar"  
    bash: export: `=': not a valid identifier
    
  • Only the exported variable (B) is seen by the subprocess:

    $ A="Alice"; export B="Bob"; echo "echo A is \$A. B is \$B" | bash
    A is . B is Bob
    
  • Changes in the subprocess do not change the main shell:

    $ export B="Bob"; echo 'B="Banana"' | bash; echo $B
    Bob
    
  • Variables marked for export have values copied when the subprocess is created:

    $ export B="Bob"; echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash &
    [1] 3306
    $ B="Banana"; echo '(sleep 30; echo "Subprocess 2 has B=$B")' | bash 
    Subprocess 1 has B=Bob
    Subprocess 2 has B=Banana
    [1]+  Done         echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash
    
  • Only exported variables become part of the environment (man environ):

     $ ALICE="Alice"; export BOB="Bob"; env | grep "ALICE\|BOB"
     BOB=Bob
    

So, now it should be as clear as is the summer's sun! Thanks to Brain Agnew, alexp, and William Prusell.


By default, variables created within a script are only available to the current shell; child processes (sub-shells) will not have access to values that have been set or modified. Allowing child processes to see the values, requires use of the export command.


Although not explicitly mentioned in the discussion, it is NOT necessary to use export when spawning a subshell from inside bash since all the variables are copied into the child process.


It should be noted that you can export a variable and later change the value. The variable's changed value will be available to child processes. Once export has been set for a variable you must do export -n <var> to remove the property.

$ K=1
$ export K
$ K=2
$ bash -c 'echo ${K-unset}'
2
$ export -n K
$ bash -c 'echo ${K-unset}'
unset

Just to show the difference between an exported variable being in the environment (env) and a non-exported variable not being in the environment:

If I do this:

$ MYNAME=Fred
$ export OURNAME=Jim

then only $OURNAME appears in the env. The variable $MYNAME is not in the env.

$ env | grep NAME
OURNAME=Jim

but the variable $MYNAME does exist in the shell

$ echo $MYNAME
Fred

The accepted answer implies this, but I'd like to make explicit the connection to shell builtins:

As mentioned already, export will make a variable available to both the shell and children. If export is not used, the variable will only be available in the shell, and only shell builtins can access it.

That is,

tango=3
env | grep tango # prints nothing, since env is a child process
set | grep tango # prints tango=3 - "type set" shows `set` is a shell builtin

Examples related to linux

grep's at sign caught as whitespace How to prevent Google Colab from disconnecting? "E: Unable to locate package python-pip" on Ubuntu 18.04 How to upgrade Python version to 3.7? Install Qt on Ubuntu Get first line of a shell command's output Cannot connect to the Docker daemon at unix:/var/run/docker.sock. Is the docker daemon running? Run bash command on jenkins pipeline How to uninstall an older PHP version from centOS7 How to update-alternatives to Python 3 without breaking apt?

Examples related to bash

Comparing a variable with a string python not working when redirecting from bash script Zipping a file in bash fails How do I prevent Conda from activating the base environment by default? Get first line of a shell command's output Fixing a systemd service 203/EXEC failure (no such file or directory) /bin/sh: apt-get: not found VSCode Change Default Terminal Run bash command on jenkins pipeline How to check if the docker engine and a docker container are running? How to switch Python versions in Terminal?

Examples related to shell

Comparing a variable with a string python not working when redirecting from bash script Get first line of a shell command's output How to run shell script file using nodejs? Run bash command on jenkins pipeline Way to create multiline comments in Bash? How to do multiline shell script in Ansible How to check if a file exists in a shell script How to check if an environment variable exists and get its value? Curl to return http status code along with the response docker entrypoint running bash script gets "permission denied"