Shell and Environment Variables

Steven Zeil

Last modified: Aug 24, 2023
Contents:

In any programming language, we expect to have variable names. The commands that you type in via a command shell constitute a programming language, though you might not think of it as one, and so it should not come as too big a surprise to discover that command shells have variables.

1 Setting and Retrieving Shell Variable Values

A typical shell variable name begins with an alphabetic character and continues with zero or more alphanumeric characters. Oddly enough, when we want to get the value of a shell variable, we add a $ to the front of it.

Example 1: Try this:

Log in and enter the following commands.

A=hello
echo $A

and the echo command will produce the string “Hello”.

All shell variables hold strings, although in selected instances we may be able to interpret those strings as numbers. It all really depends on the commands we are using. If you place a shell variable into a command at a position where the command expects a number, then it will be treated as a number.

Example 2: Try This

Give the following commands:

cd ~/playing
ls ~ > 1.txt
cat 1.txt
one=1
head $one.txt
head -n $one 1.txt

In the next-to-last command, we use the shell variable as a string, part of the filename. In the final command, we used the variable in a position where the head command expects to find a number, and so it was treated as one.

Caution: when writing commands that involve environment variables, it helps to keep in mind the different forms of quoting in Unix shells, especially the difference between ‘single quotes’ and “double quotes”.

Example 3: Try This

Give the following commands.

cd ~/playing
date > 1.dat
ls > 2.dat
A=1.dat 2.dat
echo $A
A="1.dat 2.dat"
echo $A
echo "$A"
echo '$A'
head -n 1 $A
head -n 1 "$A"
head -n 1 '$A'

Some of these commands may give error messages. Study the messages carefully until you understand what “went wrong”.

1.1 Shell Variables and Backticks

The use of backticks to insert the output of one command into another is often exploited to assign environment variables.

Example 4: Try This

Give the following commands

Today=`date`
echo $Today
Words=`grep ^ze /usr/share/dict/words`
echo $Words
echo $Words | wc

Because many Unix commands function as “filters”, taking input from standard in and producing output on standard out, we can use pipes within backticks to alter the values that would be stored in a variable.

Example 5: Try This

Give the following commands

echo $USER
uc=`echo I am $USER | tr a-z A-Z`
echo $uc
nv=`echo I am $USER | sed -e "s/[aeiou]/./ig"`
echo $nv

1.2 Manipulating File Paths

A common use of the combination of environment variables and backticks is to pull apart “pieces” of a file path.

The following commands are commonly used for this purpose:

basename filepath: Prints the last part of a path, just after the final /

dirname filepath: Extracts the part of a path just before the final /, or prints . if the path has no /s

readlink -f filepath: Prints the “canonical form” of the file path — the absolute path to the file with no unnecessary steps.

Example 6: Try This

Give the following commands:

cd ~
echo /home/$USER/playing
basename /home/$USER/playing
basename ~/playing
basename playing
basename .

dirname /home/$USER/playing
dirname ~/playing
dirname playing
dirname .

readlink -f /home/$USER/playing
readlink -f ~/playing
readlink -f playing
readlink -f .

These three commands aren’t generally something you would bother with when typing commands directly at the command line. But they can be used with backticks to put useful information into environment variables.

Example 7: Try This

Give the following commands:

cd ~
myFile=~/playing/games/math.h
ls -l $myFile
myDir=`dirname $myFile`
echo $myDir
ls -l $myDir

myDir=~/playing/games/..
ls -l $myDir
basename $myDir
dirname $myDir

Now, if we, as “intelligent” programmers, understand that ~/playing/games/.. is “really” just an awkward way of saying ~/playing. But the basename and dirname commands just aren’t that smart. That’s where the readlink command comes into play. It not only turns relative paths into absolute paths, it “canonicalizes” the path by getting rid of steps like “../” or “./”.

Give the following commands:

myDir2=`readlink -f $myDir`
echo $myDir2
ls -l $myDir2
basename $myDir2
dirname $myDir2

This combination of the directory manipulation commands, backticks, and environment variables, often proves useful when writing scripts, the subject of the final lesson in this course.

2 The Environment

The scope rules for shell variables are a bit odd. Most variables are local to the process where they are assigned. For example, look at the following (‘>’ is the prompt for a new command).

> A=hello
> echo $A
hello
> sh
> echo $A
A: undefined variable
> exit
> echo $A
hello

We set A to a value and then , using echo, printed it. In the next line, we start another copy of the shell running (sh or csh). This runs as a separate process (the old one is temporarily suspended). This new “child” process does not inherit the variable A, so when we try to print it, we are told that A is undefined. The exit command shuts down the child process, returning us to the original (parent) process where we had previously defined A, and so we were able to print it again.

This means that, normally, any environment variables that we set in this fashion will have no effect at all on any subsequent commands or programs that we launch (because all commands and programs are run as new, separate processes). Setting environment variables like this affects only the command shell in which we do it, so they can effect how that shell interprets what we type before it launches the command.

If we prefer, we can make a variable exported, meaning that its value will be seen by child processes. In sh and bash this is done by naming the variable in an export command.

> B=goodbye
> export B
> echo $B
goodbye
> sh
> echo $B
goodbye
> exit
> echo $B
goodbye

The Unix environment is the collection of all shell variable values that have been imported from a parent process. Such inherited shell variables are called environment variables. Even when you first log in, your shell may inherit a number of environment variables from the process that manages user logins.

Many Unix programs rely on exported shell variables to control or modify their behavior. Examples that may be familiar include:

TERM
Way back when we were learning to log in with ssh, we had to set the TERM variable to indicate what kind of terminal our text-mode ssh client was emulating. Many Unix programs use this setting to determine what control character sequences will work on our display.
DISPLAY
The DISPLAY variable is used by X applications to determine where to send the windows and graphics for display to the person running the application.
SHELL
This variable saves the name of the command shell program being used to interpret your typed-in commands.
Example 8: Try This

The env command lists all environment and shell variables active in your current process. Give the command

env | more

and page through the list (it may be quite long). Can you guess the meaning/purpose of most of these?

2.1 The $PATH

A particularly important environment variable is PATH. This determines what programs you can execute without typing in a full path name. If you type a command that is not a built-in shell command, and does not contain a ‘/’, then the shell looks at each directory listed in $PATH to see if the program you have requested can be found there. For example, suppose you have compiled a C++ program and produced a new program, yourProgram, in your current working directory. Some people will be able to execute the program this way:

yourProgram

while others will have to do it like this:

./yourProgram

The difference stems from whether your account has been set up so that your working directory (.) is in your $PATH. If it is, then you can use the first form. If it is not, you must use the second form. (Some people consider it a bit of a security risk to have . in your $PATH, arguing that you could get spoofed into doing some very strange things if someone had deposited some malicious programs into your working directory under innocuous names like “ls” or “cp”. My own feeling is that the threat here is pretty small.) To see your path, just give the command

echo $PATH

to print it, just as you would print any environment variable.

Adding additional directories to your $PATH is one of the more common customizations that people make to their Unix environments. This lets them set up special directories full of customized commands and frequently-used programs.