Almost everybody coming across this page will know a little about environment variables: e.g., the ubiquitous $PATH that appears both on Unix and (DOS/)Windows systems. Online discussion in connection with this blog post: Never use environment variables for configuration suggests a lot of people don’t know much more than the basics of environment variables (for understandable reasons – hardly anybody now does day-to-day work by composing whole programs).

Here are some notes on them:

Environment variables enable rapid, overlapped, change of program behaviour

Unlike with a configuration file, by using environment variables an executable can be invoked with different behaviour very rapidly and in an overlapped fashion.

For example the following will show current time in every timezone known to the system sequentially:

for TZ in `find /usr/share/zoneinfo/posix -type f -or -type l | sort`; do 
echo $TZ `TZ=$TZ date`; 
done

The same could in principle be done by changing a configuration file and invoking the program in sequence. However consider the following fragment which runs the command in parallel:

time \
find /usr/share/zoneinfo/posix -type f -or -type l | \
sort | \
xargs  parallel -i  env TZ={} date  --

Using a configuration file to do the above would be tricky as each parallel invocation would be trying to modify the same file at the same time.

Timing output is:

real	0m2.484s
user	0m0.672s
sys	0m7.094s

confirming that processes are indeed parallel (i.e. sys > real).

Nesting / dynamical scope

Bash (and some other Unix shells) have an environment variable $SHLVL which contains the nesting level of the current shell, i.e., how many times the shell has recursively started itself up to the command at hand. This variable gives an easy insight in how environment variables are conventionally used. Here is an example transcript:

$> echo $SHLVL
4
$> bash
$> echo $SHLVL
5
$> exit
exit
$> echo $SHLVL
4

What we can see that when we start a new nested shell, the $SHLVL variable is incremented by one in the new shell, however when we get back to the previous shell we also get back to the $SHLVL value.

In conventional usage environment variables are example of dynamically scoped variables see e.g. this or in CLTL nomenclature indefinite scope and dynamic extent which ends when the process in which the variable was bound exits.

Dynamically scoped variables are particularly useful for layered run-time configuration of a complex system and this is reflected in the usefulness of environment variables – they certainly are more than just simple global configuration files.