Chapter 16

Shell Scripts - Variables

Introduction

We have learned about parameters in shell scripts;
now we learn about variables.

Variables

Shell variables are a bit like parameters - they both can hold
values which consist of zero or more characters.
The differences between parameters and variables are:

Parameter names are single digits;
they are always preceded by a dollar sign but
that is not part of the name.

Variable names start with a letter and can be followed by
letters and/or digits.
Variable names occur with and without
preceding dollar signs.

Parameter are given values
before
the script starts being executed.
Variables are given values
after
the script starts being executed.

Assigning values to variables

Variables are most useful in scripts
but we will demonstrate them using the shell interactively.
This example puts a value in a variable called
file:

$ file=precious
$

and this example displays the value that is stored in
file.

$ echo the value in file is $file
the value in file is precious
$

Notice that in the first example,
there is no dollar sign in front of
file
but in the second one there is.
This is because the dollar sign means `the value of',
so
file
is the
name
of the variable,
but
$file
is the
value
of the variable.

Now you can see why we never use parameters without
a dollar sign in front of them - we only use the value of
parameters and we rarely put new values into them inside
scripts.

Empty variables and removing values

Variables do not have to be declared.
If they haven't been given a value shell
treats them as if they held nothing.
This demonstrates that:

$ echo the value in nonesuch is $nonesuch.
the value in nonesuch is .

Notice that both the space before
$nonesuch
and the full stop after it were
echoed
but nothing was output in place of the empty variable.

We can remove the value held in a variable like this:

$ file=
$ echo the value in file is $file.
the value in file is .
$

Spaces and =

Beware of trying to put spaces around the equals sign
when assigning values to variables.
This shows what happens if you try:

$ answer = 42
sh: answer: not found
$

Shell is trying to execute a command called
answer
with arguments
=
and
42.
If we just put a space after the equals sign, like this:

$ answer= 42
sh: 42: not found
$

shell empties
answer
and attempts to run a command called
42.

Uses for variables

What can we do with variables?
The main use is in more complicated scripts but
here are two simple cases where variables make life
easier.

Suppose we want to keep moving files from one
directory to another and that
the path-names of the directories
are both very long.
We can save ourselves time and effort by putting the
name of one of the directories into a variable,
like this:

$ other=/homedir/cms/ps/student/unix/examples
$

and then we could refer to the other directory with much
less typing in this fashion:

The first step in the new
bu
puts the first parameter into
file
so that the reader
no longer has to remember that
$1
contains the name of the file.
That little change makes the script much clearer.
Obviously, the more parameters we have, the more
useful the technique is!

Read

Nearly every programming language has a statement to output (display or print)
variables and/or strings in the form of text.
They also have
another statement to input values into variables.

Shell scripts output text with
echo.
Shell's input statement is
read
which takes words from a line of input and puts them into variables.
Here is a script which demonstrates the
read
statement:

$ more echoline
echo Type a line:
read line
echo the line was: $line
$

Executing it:

$ echoline
Type a line:
Small is Beautiful
the line was: Small is Beautiful
$

Avoid interactive scripts

When they learn about the
read
command, newcomers to Unix often use it in shell scripts instead of expecting
arguments on the command line.
This is unwise as it means their scripts can only be used interactively;
they can't be used in further tool-building.
Very few of the standard Unix commands are interactive.
You should write your scripts in the same way, to make them fit in with
the Unix philosophy.

Problems with echo

There are many different versions of Unix and we should write scripts
that, as far as possible, run under any version.
Possibly the biggest obstacle to portability is
echo
itself.
This is partly because there are several different versions and partly because
echo
is also built-in to most shells to aid efficiency.
The difficulty is there are subtle differences between these versions.
My advice is: only use
echo
for complete lines of plain text.
If you wish to use control characters, or output a partial line
(without a newline), use the
printf
command.

printf

C programmers will be familiar with
printf.
To see all it can do, you will need to consult its
man
page.
This example shows the main points:

The first
printf
outputs some text but leaves the cursor at the end of the line so
the user can type their reply on the same line as the prompt.

Usually,
printf
is used to fit values into a
format string.
The
secondprintf
statement demonstrates that.
The format string is enclosed in quotation marks and the value is
Tom.
In the format string, the percent sign
(%) is a place marker; it shows where the value is to be inserted
into the format string before output.
The
s, four characters after the percent sign,
indicates that the value is a
string
of characters.
The
10
between the
%
and the
s
gives the number of characters the value should occupy in the line of output.
The
-
shows the value is the be placed at the left side of the allocated space
(left justified).
The
thirdprintf
shows a
right
justified twelve character string which is followed by a
newline character
(\n).
The
lastprintf
simply shows there can be more than one place marker in the format string,
and that there can be more than one value.
Notice, there can only be
one
format string.

As well as being more portable,
printf
helps us to have tidier output.
Look how the left and right margins are lined up when
format
outputs the names.

Built-in variables

There are some pre-defined variables that
shell already has values for.
The names of these built-in variables are all in upper-case letters
so that they do not conflict with the names we might choose.

Two of the most amusing ones are called
PS1
and
PS2
which hold the primary and secondary
shell prompt strings.
You can
echo
them thus:

$ echo $PS1 $PS2
$ >
$

As you see, on my system they are set to
$
and
>.
The amusing bit is:
you can set them to whatever you like -
just like ordinary shell variables.
Here is how it's done:

There are a couple of points to watch out for.
First, it looks a complete mess if you don't end
the prompt string with a space.
Also, strange prompts are confusing, so only use
them when there is a real need.

A better way to see shell's built-in variables is to use
set
which will display them
all
- user-defined and built-in.
On my system part of the output looks like this:

HOME

You should recognise the contents of
$HOME
as being the name of your home directory.
We can use it as a shortcut to avoid remembering
or typing the home directory's full name.
For example, to access the home directory when
we are not in it, we can do this:

Some versions of the shell let you type a tilde
(~) character instead of
$HOME.
Try the following to see if it works on your system:

$ ls ~
????
$

If it doesn't work you could switch to
bash.

PATH

PATH
is more complicated:
it contains a list of directory names separated by colons (:).
The list of directories in the
PATH
shown above (/usr/local/bin:/usr/lang:/X11/bin:/usr/ucb:/usr/bin:.) is:

/usr/local/bin
/usr/lang
/X11/bin
/usr/ucb
/usr/bin
.

Whenever you enter a command, shell has to find a file with the same name
as the command.
It looks for the file in the directories listed in
PATH.
If it does not find an executable file in the first directory
from the left in
$PATH,
it looks in the next one,
and so on.
If a file with the same name is not found
in any of the directories, then you get the familiar
xyz: not found
error message.

The dot at the end of the list of directories means
the current directory.
That is why shell could not find
newcmd
when we were `hiding' in the
elsewhere
directory
in the previous chapter.
Shell was looking in
elsewhere
and the other directories in the list above, but the file
wasn't in any of them.
We will set up a customised version of
PATH
in a later chapter ?? and then we will be able to
execute our scripts whatever directory we are in.

QUESTIONS

For first three questions you have to write a shell script.
Name them `q11.1' ...

Write a shell script to put the value 42 into a variable and
display both the name and the value of the variable.

Answer

$ more q11.1
answer=42
echo The answer to the ultimate question is $answer.
$ q11.1
The answer to the ultimate question is 42.
$

Hide

Modify the script from question one so that that it then removes
the value in the variable and displays it again.

Answer

$ more q11.2
answer=42
echo The answer to the ultimate question is $answer.
answer=
echo The answer to the ultimate question is now $answer.
$ q11.2
The answer to the ultimate question is 42.
The answer to the ultimate question is now .
$

Hide

Create a shell script to prompt for a directory name, and then
display the number of things (files plus directories) in the
directory.

Notice: the
date
command is split over two lines so the effect of the second prompt can be seen.

You can't set the prompts with a script because the non-interactive
shell that runs the script is a different shell to the interactive one
that is prompting you and reading your keyboard input.

Hide

Reset the shell prompts to normal.

Answer

hoho PS1='$ '
$ PS2='> '
$ da\
> te
Wed Jul 8 22:19:59 BST 2009
$

Hide

ANSWERS

In the first three answers the script is displayed using
more; then it is executed by typing its name.

$ more q11.1
answer=42
echo The answer to the ultimate question is $answer.
$ q11.1
The answer to the ultimate question is 42.
$

$ more q11.2
answer=42
echo The answer to the ultimate question is $answer.
answer=
echo The answer to the ultimate question is now $answer.
$ q11.2
The answer to the ultimate question is 42.
The answer to the ultimate question is now .
$