= Environment Variables = Every process has its own copy of the '''environment'''. This is a simply an unordered set of variables, or key/value pairs, which are copied from each parent process to its child. Environment variables are the cause of much confusion. People sometimes think of "the environment" as a global system-wide pool of settings that processes dip into (like the "registry" in Windows). This is incorrect. To summarize: * Environment variables (shown with `env`) are '''not''' the same thing as bash parameters (shown with `set`). Bash parameters are sometimes simply called "shell variables" to differentiate them from environment variables. * Environment variables are '''not global'''. Every process has its own set. * They are only copied from the parent when the child process is '''created'''. * You '''cannot change''' another process's variables (not even your children or your parent). You can only modify your '''own''' env vars. == What is the "environment"? == The environment is an unordered set of variables in an area of memory for each [[ProcessManagement|process]]. When one process creates a new process (by calling `fork()`), the child's environment is copied from the parent's environment. As a result, the new process has an environment that is identical to, but a copy of, its parent's. What this means to us bash users is that whenever we start a new process from our bash scripts, that processes will '''inherit''' our script's environment. It's important to know that we're talking about a copy here, and that this copying only ever happens during the creation of the new process. The same environment copying applies not only to processes started from your bash scripts, but to ''any'' process started on your system. Each one inherits the environment of its parent process; after that, it can add, modify or remove variables from its copy. Any children it spawns will inherit that modified environment. == How do I make environment variables? == Normally, you use the `export` command. {{{ FOOPATH=$HOME/foo:/usr/local/share/foo:/opt/foo export FOOPATH }}} This places the variable `FOOPATH` in the script's environment, to be inherited by all future child processes. If you want an environment variable to exist only for ''one'' specific child process, you can prefix any simple command with assignments: {{{ FOOPATH=$HOME/foo:/usr/local/share/foo:/opt/foo foo arg1 arg2 }}} This places `FOOPATH` in the environment of `foo` without permanently affecting the current script. Any future children won't see it. Of course, any children of `foo` ''will'' see it, unless `foo` changes its own environment. Another way to create environment variables only for one child process is to use the `env` command. This is necessary in csh and tcsh, which cannot put assignments in front of simple commands: {{{ env FOOPATH=$HOME/foo:/usr/local/share/foo:/opt/foo foo arg1 arg2 }}} You may sometimes see documents which prefer this use of `env`, because these documents are written to be as shell-agnostic as possible. In Bourne family shells (like bash), you can skip the `env`. Also note: environment variables are traditionally written in all uppercase. This is not a strict requirement; it is merely a convention. Using uppercase for environment variables and lowercase (or mixed case) for regular shell variables helps you avoid unfortunate accidents. == How do I see the environment? == In many languages, the environment variables are stored and accessed separately from regular variables, often in an associative array (or hash or dictionary). In bash, all variables (shell and environment) are simply mixed in the same namespace, making it harder to show only the environment variables. Thus, the easiest way to see the environment that you will pass on to your children is to actually ''create a child process'' and let that child display the environment it receives from the shell. The `env` command with no arguments does this: {{{ $ env SHELL=/bin/bash TERM=rxvt-unicode USER=greg LS_COLORS=ln=31:ex=35:cd=44;37:bd=44;37:pi=32 PATH=/home/greg/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/sbin:/usr/sbin:/usr/games ... }}} (This is a greatly abbreviated list.) In bash, the `export` command with no arguments can also be used to show variables that would be inherited by children: {{{ $ export ... declare -x DISPLAY=":0" declare -x EDITOR="vim" ... declare -x HOME="/home/greg" declare -x HOSTDISPLAY="titan:0" ... }}} (Again, the list is abbreviated.) == I'm still confused! Illustrate! == First, let's make sure that the Bash parameters `MY_ENV_VAR` and `myParameter` do not already exist. Assuming the parent of the current shell didn't have said variables in their environment and you did not create them yet, this step is not really necessary (doesn't hurt though). In our output, we'll use the tag "parent" to mark the '''current shell's output''', and the tag "child" to mark output generated by a '''child process'''. {{{ unset MY_ENV_VAR myParameter # Remove our variables (also from environment) echo "[parent] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [parent] MY_ENV_VAR: echo "[parent] myParameter: $myParameter" # Prints: [parent] myParameter: }}} Now, let's create some variables. These commands will create two '''Bash parameters''' called `MY_ENV_VAR` and `myParameter`, and then '''link''' `MY_ENV_VAR` to Bash's '''environment''', causing it to '''create an env var of the same name with matching content'''. We will ''not'' `export myParameter`, so ''that one exists purely as a Bash parameter''. The '''`declare -p`''' builtin command shows this. {{{ MY_ENV_VAR=Hello # Create a parameter MY_ENV_VAR myParameter=Hello # Create a parameter myParameter export MY_ENV_VAR # Link MY_ENV_VAR to the environment echo "[parent] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [parent] MY_ENV_VAR: Hello echo "[parent] myParameter: $myParameter" # Prints: [parent] myParameter: Hello declare -p MY_ENV_VAR # Prints: declare -x MY_ENV_VAR="Hello" declare -p myParameter # Prints: declare -- myParameter="Hello" }}} Here's how we would '''update our env var'''. We basically '''update the Bash parameters'''. The parameter that's linked to the environment will cause Bash to update its env var of the same name as well (if a variable is already exported, you don't even really need `export` again). {{{ export MY_ENV_VAR=Bye myParameter=Bye echo "[parent] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [parent] MY_ENV_VAR: Bye echo "[parent] myParameter: $myParameter" # Prints: [parent] myParameter: Bye }}} Note that these `echo` statements are '''expanding our Bash parameters, not environment variables'''. That's why we see both expansions resulting in "Bye" even though only `MY_ENV_VAR` is linked to an environment variable. Let's demonstrate the effects of the environment now. As I said, when we '''create a new process''', we '''copy the environment''' into the ''new'' process' environment. The environment holds only `MY_ENV_VAR`, not `myParameter` (since we only exported the former). Here, we create a new Bash process that runs some bash code which will expand our parameters again. The new Bash process has noticed its environment contains an environment variable named `MY_ENV_VAR`, so '''it has created a Bash parameter with the same name'''. Since there is no `myParameter` on the environment, ''it hasn't created this parameter'', and it expands empty. {{{ bash -c 'echo "[child] MY_ENV_VAR: $MY_ENV_VAR"' # Prints: [child] MY_ENV_VAR: Bye bash -c 'echo "[child] myParameter: $myParameter"' # Prints: [child] myParameter: }}} Our child has only `MY_ENV_VAR` but back in our shell, we still have ''both'' parameters. Let's demonstrate that a child Bash process' version of `MY_ENV_VAR` is '''not the same''' as the shell's version of it. Here, we modify `MY_ENV_VAR` in the child, which will update the child's environment variable of the same name. But when we look at the shell's environment variable, we still find the old value: The child can update its own environment, but that '''does not affect the parent's copy'''. {{{ echo "[parent] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [parent] MY_ENV_VAR: Bye bash -c ' echo "[child] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [child] MY_ENV_VAR: Bye MY_ENV_VAR="Hello again" # Update the child's MY_ENV_VAR echo "[child] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [child] MY_ENV_VAR: Hello again ' echo "[parent] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [parent] MY_ENV_VAR: Bye }}} Let's try a trick now to demonstrate the reverse: Updating `MY_ENV_VAR` in the parent also '''doesn't update the child's''' `MY_ENV_VAR`. Here, we'll start a child process that shows its own version of `MY_ENV_VAR`, waits a little while, and then shows it again. While the child is waiting, we'll update `MY_ENV_VAR` in our shell's copy. This change will not affect our child's second output of `MY_ENV_VAR`, since the child's version of it is a ''copy at the time of the child's creation'' and '''any changes to it after it was copied are not carried over'''. {{{ echo "[parent] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [parent] MY_ENV_VAR: Bye bash -c ' echo "[child] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [child] MY_ENV_VAR: Bye sleep 5 # Wait 5 seconds. (adjust according to typing speed) echo "[child] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [child] MY_ENV_VAR: Bye ' & MY_ENV_VAR="Hello again" # Update the parent's MY_ENV_VAR echo "[parent] MY_ENV_VAR: $MY_ENV_VAR" # Prints: [parent] MY_ENV_VAR: Hello again # Output: [parent] MY_ENV_VAR: Bye [child] MY_ENV_VAR: Bye [parent] MY_ENV_VAR: Hello again [child] MY_ENV_VAR: Bye }}} As you can see, having updated the parent's `MY_ENV_VAR` while the child was waiting ''has not changed'' the child's version of `MY_ENV_VAR`. == But subshells are different == A SubShell is a child process, and so it inherits the parent's environment; but it's special, and it ''also'' inherits the parent's regular parameters. {{{ $ unset myParameter $ myParameter=42 $ (echo "$myParameter") 42 $ bash -c 'echo "$myParameter"' $ }}} However, the subshell still can't alter the parent's parameters, because it ''is'' still just a child. {{{ $ myParameter=42 $ (myParameter=69) $ echo "$myParameter" 42 }}}