Understanding Unix shells and environment variables

Unix Insider –

A shell variable is a memory storage area that can be used to hold a value, which can then be used by any built-in shell command within a single shell. An environment variable is a shell variable that has been exported or published to the environment by a shell command so that shells and shell scripts executed below the parent shell also have access to the variable.

One built-in shell command can set a shell variable value, while another can pick it up. In the following

<font face="Courier">doecho</font>
script example,
<font face="Courier">$PLACE</font>
is set in the first line and picked up in the second line by the built-in
<font face="Courier">echo</font>
command.

Create this script and save it as

<font face="Courier">doecho</font>
. Change the mode using
<font face="Courier">chmod a+x doecho</font>
:

<font face="Courier"># doecho sample variable
PLACE=Hollywood
echo "doecho says Hello " $PLACE
</font>

Run the program as shown below.

In all of the following examples, I use the convention of

<font face="Courier">./command</font>
to execute a shell script in the current directory. You don't need to do this if your
<font face="Courier">$PATH</font>
variable contains the
<font face="Courier">.</font>
as one of the searched directories. The
<font face="Courier">./command</font>
method works for scripts in your current directory, even if the current directory isn't included on your path.

<font face="Courier">$ ./doecho
doecho says Hello Hollywood
$
</font>

In this first example,

<font face="Courier">$PLACE</font>
is a shell variable.

Now, create another shell script called

<font face="Courier">echoplace</font>
and change its mode to executable.

<font face="Courier"># echoplace echo $PLACE variable
echo "echoplace says Hello " $PLACE
</font>

Modify

<font face="Courier">doecho</font>
to execute
<font face="Courier">echoplace</font>
as its last step.

<font face="Courier"># doecho sample variable
PLACE=Hollywood
echo "doecho says Hello " $PLACE
./echoplace
</font>

Run the

<font face="Courier">doecho</font>
script. The output is a bit surprising.

<font face="Courier">$ ./doecho
doecho says Hello Hollywood
echoplace says Hello
$
</font>

In this example,

<font face="Courier">echoplace</font>
is run as the last command of
<font face="Courier">doecho</font>
. It tries to echo the
<font face="Courier">$PLACE</font>
variable but comes up blank. Say goodbye to Hollywood.

To understand what happened here you need understand something about shell invocation -- the sequence of events that occur when you run a shell or shell script. When a shell begins to execute any command, it checks to see if the command is built-in (like

<font face="Courier">echo</font>
), an executable program (like vi or
<font face="Courier">grep</font>
), a user-defined function, or an executable shell script. If it's any of the first three, it directly executes the command, function, or program; but if the command is an executable shell script, the shell spawns another running copy of itself -- a child shell. The spawned child shell uses the shell script as an input file and reads it in line by line as commands to execute.

When you type

<font face="Courier">./doecho</font>
to execute the
<font face="Courier">doecho</font>
script, you're actually executing a command that is something like one of the following, depending on which shell you're using.

<font face="Courier">$ sh < ./doecho
            (or)
$ ksh <./doecho
</font>

The new shell, spawned as a child of your starting-level shell, opens

<font face="Courier">doecho</font>
and begins reading commands from that file. It performs the same test on each command, looking for built-in commands, functions, programs, or shell scripts. Each time a shell script is encountered, another copy of the shell is spawned.

I have repeated the running of

<font face="Courier">doecho</font>
so you can follow it through the steps described below. The output of
<font face="Courier">doecho</font>
is repeated here, with extra spacing and notes.

<font face="Courier">$ ./doecho                  <-the command typed in shell one
                              launches shell two reading doecho.
doecho says Hello Hollywood <-shell two sets $PLACE and echoes
                              Shell three starts echoplace
echoplace says Hello        <-shell three cannot find $PLACE and
                              echoes a blank
$                           <-shells three and two exit. Back at shell one
</font>

As you're looking at a prompt on the screen, you're actually running a top-level shell. If you've just logged on, this will be shell one, where you type the command

<font face="Courier">./doecho</font>
. Shell two is started as a child of shell one. Its job is to read and execute
<font face="Courier">doecho</font>
. The
<font face="Courier">doecho</font>
script is repeated below.

The first command in

<font face="Courier">doecho</font>
creates the shell variable
<font face="Courier">$PLACE</font>
and assigns the value "Hollywood" to it. At this point, the
<font face="Courier">$PLACE</font>
variable only exists with this assignment inside shell two. The
<font face="Courier">echo</font>
command on the next line will print out
<font face="Courier">doecho says Hello Hollywood</font>
and move on to the last line. Shell two reads in the line containing
<font face="Courier">./echoplace</font>
and recognizes this as a shell script. Shell two launches shell three as a child process, and shell three begins reading the commands in
<font face="Courier">echoplace</font>
.

<font face="Courier"># doecho sample variable
PLACE=Hollywood
echo "doecho says Hello " $PLACE
./echoplace
</font>

The

<font face="Courier">echoplace</font>
shell script is repeated below. The only executable line in
<font face="Courier">echoplace</font>
is a repeat of the echoed message. However,
<font face="Courier">$PLACE</font>
only exists with the value "Hollywood" in shell two. Shell three sees the line to echo
<font face="Courier">echoplace says Hello</font>
and the
<font face="Courier">$PLACE</font>
variable, and cannot find any value for
<font face="Courier">$PLACE</font>
. Shell three creates its own local variable named
<font face="Courier">$PLACE</font>
as an empty variable. When it echoes the script, it's empty and prints nothing.

<font face="Courier"># echoplace echo $PLACE variable
echo "echoplace says Hello " $PLACE
</font>

The assignment of "Hollywood" to

<font face="Courier">$PLACE</font>
in shell two is only available inside shell two. If you type in a final command in shell one to echo
<font face="Courier">$PLACE</font>
at the shell one level, you'll find that
<font face="Courier">$PLACE</font>
is also blank in shell one.

<font face="Courier">$ echo "shell one says Hello " $PLACE
shell one says Hello
$
</font>

Thus far, you've only created and used a variable inside of a single shell level. You can, however, publish a shell variable to the environment, thereby creating an environment variable that's available both to the shell that published it and to all child shells started by the publishing shell. Use

<font face="Courier">export</font>
in the Bourne and Korn shells.

<font face="Courier">$ PLACE=Hollywood; export PLACE
$
</font>

The Korn shell also has a command that both exports the variable and assigns a value to it.

<font face="Courier">$ export PLACE=Hollywood
$
</font>

The C shell uses a very different syntax for shell and environment variables. Assign a value to a shell variable by using

<font face="Courier">set</font>
, then assign an environment variable using
<font face="Courier">setenv</font>
. Note that
<font face="Courier">setenv</font>
doesn't use the
<font face="Courier">=</font>
operator.

<font face="Courier">> set PLACE=Hollywood
> setenv PLACE Hollywood
</font>

Back in the Korn or Bourne shells, if we revisit the

<font face="Courier">doecho</font>
script and edit it to export the
<font face="Courier">$PLACE</font>
variable, it becomes available in shell two (the publishing shell) and shell three (the child shell).

<font face="Courier"># doecho sample variable
PLACE=Hollywood; export PLACE
echo "doecho says Hello " $PLACE
./echoplace
</font>

When

<font face="Courier">doecho</font>
is run, the output is changed. This happens because in shell three
<font face="Courier">$PLACE</font>
is found as an environment variable that has been exported from shell two.

<font face="Courier">$ ./doecho
doecho says Hello Hollywood
echoplace says Hello Hollywood
$
</font>

Assigning a value to

<font face="Courier">$PLACE</font>
before you run
<font face="Courier">doecho</font>
will help you verify its scope. After
<font face="Courier">doecho</font>
is complete, echo the value of
<font face="Courier">$PLACE</font>
at the shell one level. Notice that
<font face="Courier">doecho</font>
in shell two and
<font face="Courier">echoplace</font>
in shell three both see
<font face="Courier">$PLACE</font>
's value as "Hollywood", but the top-level shell sees the value "Burbank". This is because
<font face="Courier">$PLACE</font>
was exported in shell two. The environment variable
<font face="Courier">$PLACE</font>
has scope in shell two and shell three, but not in shell one. Shell one creates its own local shell variable named
<font face="Courier">$PLACE</font>
that is unaffected by shells two and three.

<font face="Courier">$ PLACE=Burbank
$ ./doecho
doecho says Hello Hollywood
echoplace says Hello Hollywood
$ echo "shell one says Hello " $PLACE
$ shell one says Hello Burbank
$
</font>

Once a shell variable has been exported and become an environment variable, it can be modified by a subshell. The modification affects the environment variable at all levels where the environment variable has scope.

Make some changes to

<font face="Courier">doecho</font>
by adding a repeat of the
<font face="Courier">echo</font>
line after the return from
<font face="Courier">echoplace</font>
.

<font face="Courier"># doecho sample variable
PLACE=Hollywood
echo "doecho says Hello " $PLACE
./echoplace
echo "doecho says Hello " $PLACE
</font>

After it has been echoed, modify

<font face="Courier">echoplace</font>
to change the value of
<font face="Courier">$PLACE</font>
. Once this is done, echo it again.

<font face="Courier"># echoplace echo $PLACE variable
echo "echoplace says Hello " $PLACE
PLACE=Pasadena
echo "echoplace says Hello " $PLACE
</font>

Retype the previous sequence of commands as shown below. Shell three alters the value of

<font face="Courier">$PLACE</font>
, a change that appears in shell three -- and in shell two, even after it returns from
<font face="Courier">echoplace</font>
. Once a variable is published to the environment, it's fair game to any shell at or below the publishing level.

<font face="Courier">$ PLACE=Burbank
$ ./doecho
doecho says Hello Hollywood
echoplace says Hello Hollywood
echoplace says Hello Pasadena
doecho says Hello Pasadena
$ echo "shell one says Hello " $PLACE
$ shell one says Hello Burbank
$
</font>

You have seen that the default action of a shell is to spawn a child shell whenever a shell script is encountered on the command line. Such behavior can be suppressed by using the dot command, which is a dot and a space placed before a command.

Execute

<font face="Courier">doecho</font>
by starting it with a dot and a space, then echo the value of
<font face="Courier">$PLACE</font>
when
<font face="Courier">doecho</font>
is complete. In this example, shell one recognizes
<font face="Courier">$PLACE</font>
as having been given the value "Pasadena".

<font face="Courier">$ . ./doecho
doecho says Hello Hollywood
echoplace says Hello Hollywood
echoplace says Hello Pasadena
doecho says Hello Pasadena
$ echo "shell one says Hello " $PLACE
$ shell one says Hello Pasadena
$
</font>

Normally, when a shell discovers that the command to execute is a shell script, it would spawn a child shell and that child would read in the script as commands. If the shell script is preceded by a dot and a space, however, the shell stops reading the current script or commands and starts reading in the new script or commands without starting a new subshell.

1 2 Page
What’s wrong? The new clean desk test
View Comments
You Might Like
Join the discussion
Be the first to comment on this article. Our Commenting Policies