We have learned about parameters in shell scripts; Now we learn about 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.
Parameters are given values before the script starts being executed. Variables are given values after the script starts being executed.
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.
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 . $
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 parameters
=
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.
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:
$ ls $other/scripts /homedir/cms/ps/student/unix/examples/scripts: No such file or directory $ mv scripts $other $
Another use is to make the
bu
script easier to read.
Here is an improved version:
$ more bu
file=$1
cp $file $file.bu
echo $file backed-up in $file.bu
$
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!
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 $
shows a line of input being read into a variable and displayed.
If we want to extract individual words we can do so:
$ more echoword3 read word1 word2 word3 echo word3 is: $word3 $ echoword3 E F Schumacher word3 is: Schumacher $
If more words are supplied in the input than there are variables in
the
read
command, shell adds the extra ones onto the
last variable:
$ echoword3 Small is Beautiful by E F Schumacher word3 is: Beautiful by E F Schumacher $
When they learn about the
read
command, newcomers to Unix often use it in shell scripts instead of accepting
parameters from the command line.
This is unwise as it means their scripts can only be used interactively,
and 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.
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.
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:
$ cat format
printf 'A user prompt: '
read reply
printf '%-10s ' Tom
printf '%11s (Mr)\n' Chomondeley
printf '%-10s %11s (Ms)\n' Cassandra Short
$
Executing the
format
script, we see this:
$ format A user prompt: user reply Tom Chomondeley (Mr) Cassandra Short (Ms) $
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
second
printf
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
third
printf
shows a
right
justified eleven character string which is followed by a
newline character
(\n).
The
last
printf
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.
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:
$ PS1='Yeah? ' Yeah? PS2='gimme more! ' Yeah? echo 'one gimme more! two gimme more! three gimme more! lines' one two three lines Yeah? PS1='$ ' $ PS2='> ' $
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:
$ set
DISPLAY=:0.0
EXINIT=set showmode
HOME=/homedir/cms/ps
IFS=
LD_LIBRARY_PATH=/X11/lib:/ext01/IXI/lib
LOGNAME=cmsps
MAILCHECK=600
MANPATH=/usr/local/man:/usr/lang/man:/usr/man:/X11/man
OPTIND=1
PAGER=more
PATH=/usr/local/bin:/usr/lang:/X11/bin:/usr/ucb:/usr/bin:.
PRINTER=lp
SHELL=/bin/sh
TERM=xterm
TERMCAP=vs|xterm|vs100|xterm ....... etc ...........
USER=cmsps
WINDOWID=25165837
XLIBDIR=/ext01/IXI/lib/X11
$
Of these, two of the most important ones are
HOME
and
PATH.
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:
$ pwd /homedir/cms/ps/book.unix $ ls $HOME bin book.c++ book.pascal book.unix jobs lastdir logins macro $
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
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.
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. $
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 . $
Create a shell script to prompt for a directory name, and then display the number of things (files plus directories) in the directory.
Answer
$ more q11.3 printf 'Directory? ' read directory cd $directory ls | wc -l $ q11.3 Directory? red 21 $
Start a spare
xterm
and set the shell prompts to something
amusing.
Note that you can't do that with a script.
Why not?
Answer
$ PS1='hoho ' hoho PS2='heehee ' hoho da\ heehee te Wed Jul 8 22:19:11 BST 2009 hoho
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.
Reset the shell prompts to normal.
Answer
hoho PS1='$ ' $ PS2='> ' $ da\ > te Wed Jul 8 22:19:59 BST 2009 $
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 . $
$ more q11.3 printf 'Directory? ' read directory cd $directory ls | wc -l $ q11.3 Directory? red 21 $
$ PS1='hoho ' hoho PS2='heehee ' hoho da\ heehee te Wed Jul 8 22:19:11 BST 2009 hoho
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.
hoho PS1='$ ' $ PS2='> ' $ da\ > te Wed Jul 8 22:19:59 BST 2009 $
http://homepages.shu.ac.uk/~cmsps/unix/variables.html
Last updated: Thursday 05 April 2012 at 17:45