Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page > Hide TOC

Exporting Shell Variables

One key feature of shell scripts is that they are limited in their scope to the currently running script. The scoping of variables is described in more detail in “Result Codes, Subroutines, Scoping, and Sourcing.” For now, though, it suffices to say that variables generally do not get passed on to scripts or tools that they execute.

Normally, this is what you want. Most variables in a shell script do not have any meaning to the tools that they execute, and would thus just represent clutter and the potential for variable namespace collisions if they are exported. Occasionally, however, you will find it necessary to make a variable's value available to a tool. To do this, you must export the variable. These exported variables are commonly known as environment variables because they are outside the scope of the script itself, but affect its execution.

A classic example of an environment variable that is significant to scripts and tools is the PATH variable. This variable specifies a list of locations that the shell searches when executing programs by name (without specifying a complete path). For example, when you type ls on the command line, the shell searches in the locations specified in PATH (in the order specified) until it finds an executable called ls (or runs out of locations, whichever comes first).

The details of exporting shell variables differ considerably between the Bourne shell and the C shell. Thus, the following sections explain these details in a shell-specific fashion.

In this section:

Using the export Builtin (Bourne Shell)
Overriding Environment Variables for Child Processes (Bourne Shell)
Using the setenv Builtin (C shell)
Overriding Environment Variables for Child Processes (C Shell)


Using the export Builtin (Bourne Shell)

Generally speaking, the first time you assign a value to an environment variable such as the PATH variable, the Bourne shell creates a new, local copy of this shell variable that is specific to your script. Any tool executed from your script is passed the original value of PATH inherited from whatever script, tool, or shell that launched it.

With the BASH shell, however, any variable inherited from the environment is automatically exported by the shell. Thus, in some versions of Mac OS X, if you modify inherited environment variables (such as PATH) in a script, your local changes will be seen automatically by any tool or script that your script executes. Thus, in these versions of Mac OS X, you do not have to explicitly use the export statement when modifying the PATH variable.

Because different Bourne shell variants handle these external environment variables differently (even among different versions of Mac OS X), this creates two minor portability problems:

To guarantee that your modifications to a shell variable are passed to any script or tool that your shell script calls, you must use the export command. You do not have to use this command every time you change the value; the variable remains exported until the shell script exits.

For example:

export PATH="/usr/local/bin:$PATH"
# or
PATH="/usr/local/bin:$PATH"
export PATH

Either of these statements has the same effect—specifically, they export the local notion of the PATH environment variable to any command that your script executes from now on. There is a small catch, however. You cannot later undo this export to restore the original global declaration. Thus, if you need to retain the original value, you must store it somewhere yourself.

In the following example, the script stores the original value of the PATH environment variable, exports an altered version, executes a command, and restores the old version.

ORIGPATH="$PATH"
PATH="/usr/local/bin:$PATH"
export PATH
# Execute some command here---perhaps a
# modified ls command....
ls
PATH="$ORIGPATH"

If you need to find out whether an environment variable (whether inherited by your script or explicitly set with the export directive) was set to empty or was never set in the first place, you can use the printenv command to obtain a complete list of defined variables and use grep to see if it is in the list. (You should note that although printenv is a csh builtin, it is also a standalone command in /usr/bin.)

For example:

DEFINED=`printenv | grep -c '^VARIABLE='`

The resulting variable will contain 1 if the variable is defined in the environment or 0 if it is not.

Overriding Environment Variables for Child Processes (Bourne Shell)

Because the BASH Bourne shell variant automatically exports all variables inherited from its environment, any changes you make to preexisting environment variables such as PATH are automatically inherited by any tool or script that your script executes.

While this is usually convenient, you may sometimes wish to change a preexisting environment variable without modifying the environment of any script or tool that your script executes. For example, if your script executes a number of tools in /usr/local/bin, it may be convenient to change the value of PATH to include /usr/local/bin. However, you may not want child processes to also look in /usr/local/bin.

This is easily solved by overriding the environment variable PATH on a per-execution basis. Consider the following script:

#!/bin/sh
 
echo $MYVAR

This script prints the value of the variable MYVAR. Normally, this would be a blank line. Save the script as printmyvar.sh, then type the following commands:

chmod a+x printmyvar.sh
MYVAR=7 ./printmyvar.sh
echo "MYVAR IS $MYVAR"

The initial assignment statement applies to the command that follows it. You should notice that the echo statement afterwards prints an empty string for the value of MYVAR. The value of MYVAR is only altered in the environment of the command ./printmyvar.sh, and the original (empty) value is restored after executing that command.

Thus, to modify the PATH variable locally but execute a command with the original PATH value, you can write a script like this:

#!/bin/sh
GLOBAL_PATH="$PATH"
PATH=/usr/local/bin
 
PATH="$GLOBAL_PATH" /bin/ls

Using the setenv Builtin (C shell)

In the C shell, variables are exported if you set them with setenv, but not it you set them with set. Thus, if you want your shell variable modifications to be seen by any tool or script that you call, you should use the setenv builtin. This is equivalent to an assignment statement with the export command in the Bourne shell.

setenv VALUE "Four"
echo "VALUE is '$VALUE'."

If you want your shell variables to only be available to your script, you should use the set builtin (described in “Shell Variables and Printing”). The set builtin is equivalent to a simple assignment statement in the Bourne shell.

set VALUE = "Four"
echo "VALUE is '$VALUE'."

Notice that the local variable version requires an equals sign (=), but the exported environment version does not (and produces an error if you put one in).

To remove variables in the C shell, you can use the unsetenv or unset builtin. For example:

setenv VALUE "Four"
unsetenv VALUE
 
set VALUE = "Four"
unset VALUE
 
echo "VALUE is '$VALUE'."

This will generate an error message. In the C shell, it is not possible to print the value of an undefined variable, so if you think you may need to print the value later, you should set it to an empty string rather than using unset or unsetenv.

If you need to test an environment variable (not a shell-local variable) that may or may not be part of your environment (a variable set by whatever process called your script), you can use the printenv builtin. This prints the value of a variable if set, but prints nothing if the variable is not set, and thus behaves just like the variable would behave in the Bourne shell.

For example:

set X = `printenv VALUE`
echo "X is "\"$X\"

This prints X is "" if the variable is either empty or undefined. Otherwise, it prints the value of the variable between the quotation marks.

If you need to find out if a variable is simply empty or is actually not set, you can also use printenv to obtain a complete list of defined variables and use grep to see if it is in the list. For example:

set DEFINED = `printenv | grep -c '^VARIABLE='`

The resulting variable will contain 1 if the variable is defined in the environment or 0 if it is not.

Overriding Environment Variables for Child Processes (C Shell)

Unlike the Bourne shell, the C shell does not provide a built-in syntax for overriding environment variables when executing external commands. However, it is possible to simulate this either by using the env(1) command.

The best and simplest way to do this is by using the env command. For example:

env PATH="/usr/local/bin" /bin/ls

As an alternative, you can use the set builtin to make a temporary copy of any variable you need to override, change the value, execute the command, and restore the value from the temporary copy.

You should notice, however, that whether you use the env command or manually make a copy, the PATH variable is altered prior to searching for the command. Because the PATH variable controls where the shell looks for programs to execute, you must therefore explicitly provide a complete path to the ls command or it will not be found (unless you have a copy in /usr/local/bin, of course). The PATH environment variable is explained in “Special Shell Variables.”

As a workaround, you can determine the path of the executable using the which command prior to altering the PATH environment variable.

set GLOBAL_PATH = "$PATH"
set LS = `which ls`
setenv PATH "/usr/local/bin"
$LS
setenv PATH "$GLOBAL_PATH"
unset GLOBAL_PATH

Or, using env:

set LS = `which ls`
env PATH='/usr/local/bin' $LS

The use of the back-tick (`) operator in this fashion is described in “Inline Execution.”

Security Note: If your purpose for overriding an environment variable is to prevent disclosure of sensitive information to a potentially untrusted process, you should be aware that if you use setenv for the copy, the called process will have access to that temporary copy just as it would have access to the original variable.

To avoid this, be sure to create the temporary copy using the set builtin instead of setenv.



< Previous PageNext Page > Hide TOC


Last updated: 2008-04-08




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice