[Appendix A] [Top page] [Previous Chapter]

CHAPTER 15 - SHELL PROGRAMMING


Shell commands can be stored in a file which can be executed when required. A file containing shell commands is known as a script. For example:

% cat > list	-create the filepwd
ls
^D
% chmod u+x list	-give execute permission
% list	-execute the script
/home/sunserv2_a/lnp5jb
mbox    message  list    bin

Control structures

As mentioned earlier you are strongly recommended to carry out all shell programming in the Bourne Shell. This does not mean that you have to be running a Bourne Shell when you start a script. By default, all shell scripts are normally executed by the Bourne shell, whatever your normal interactive shell. This is possible because when you run a script a new shell is started ('spawned' according to the jargon) to run the commands. You can add (as the first line):

#!  /bin/sh

to ensure that it is a Bourne Shell script. A C shell script would begin:

#!  /bin/csh

Command parameters

The Bourne shell is capable of using parameters (see the section on parameters in the previous chapter.) These may be defined by the attribution operator =, by the read command and by the for command. The Bourne shell also interprets parameters which are given as arguments to the command that executes the shell script. Such parameters are 'positional parameters', which means that they are interpreted as a list structure. This can be seen in the simple example below:

% ex simple	- first create the script
"simple" [New file]
:a
echo $1
echo $2
echo $3
.
:wq
"simple" [New file] 3 lines, 24 characters
% chmod u+x simple	- make it executable
% simple one two three	- execute it
one
two
three
%

The three arguments given to the script ('one', 'two' and 'three') are read in by the script as variables named 1, 2 and 3, and so are referred to in the script as $1, $2 and $3 respectively. The special parameter * refers to all of the parameters, and the special parameter # refers to the number of parameters.

% ex simple2	- create a new script
"simple2" [New file]
:a
echo $*
echo $#
:wq
"simple" [New file] 3 lines, 51 characters
% chmod u+x simple2
% simple2 one two three
one two three
3
%

read

The read command enables parameter values to be entered interactively by the user while the script is running. It is usual to provide a prompt for the user, as in the script listed below (called greeting):

echo "What's your name?"
read name
echo "Hello, $name"

This can give the following results:

% greeting	- execute the script
What's your name?	- output
Jenny	- the shell waits for your input
Hello, Jenny	- output%

More than one parameter can be given to the read command, usually separated by one or more spaces, as in the following script (called count):

echo How far can you count?
read first second third
echo $first $second $third

which can run to give the following:

% count
How far can you count?
1 2 3	- user input
1 2 3	- script output

PRACTICE


See what happens with this script if you give it less than three parameters. Try it with more than three - is this what you expected? Can you explain this?

Try changing the script so that it echoes each parameter on a different line. This should show what is going on.

Control structures

Sometimes it is useful to use control structures (like you find in programming languages), for example specifying that a command is only carried out under certain conditions, or that it does the same thing to a list of arguments. The shell provides control of flow with the following statements:

if structured control branching

case multiway branching

for looping over a list of commands

while conditional looping

until conditional looping

if...then...else...fi

This structure allows conditional branching. It takes the following form:

if command_list_1
  then command_list_2
  [else command_list_3]	-this clause is optional
fi

Note that it is usual practice to indent the subordinate clauses, to make the script easier to read, but it is not necessary. This structure depends on the exit status of command_list_1. Every time a command runs it returns a 0 (also known as a 'true result') if it completes it's run successfully or a 1 ('false') if fails to end normally. The command_list_2 if and only if the exit status of the last command in command_list_1 is 0 (or true). The command_list_3 is executed if and only if the exit status of command_list_1 is 1 (or false).

The test command is often used to generate an exit result. Equivalence operators may also be used such as = (equals) or != (not equal to). The following example shows the script trio in action:

% cat trio
if test $ = 3
  then echo "There are three parameters."
fi
% trio one two three
There are three parameters.
% trio one two
%							- No output

test

The test command can be used in its simplest form to test if a string exists (more exactly, if it is a 'null string' or not), as in the following script:

% cat test.1
echo "Type something please:"
read a
if test $a
  then echo "Thank you"
  else echo "Thanks for nothing"
fi
% test.1
Type something please:
Hello
Thank you
% test.1
Type something please:
Thanks for nothing
%

There are also several options that can be used in a command of the form:

test [options] filename

The following options are available:

-d true if a file is a directory

-h true if a file is a symbolic link

-x true if file exists and is executable

-l tests the length of a string

-f true if the file exists

-r true if the file can be read

-s true if the file exists and is not empty

-w true if the file can be written to

= is equal to

!= is not equal to

There are also the following arithmetic operators which apply to integer values:

-eq is equal to

-ne is not equal to

-gt is greater than

-ge is greater than or equal to

-lt is less then

-le is less than or equal to

Note that the above operators are all for use with the test command, and cannot be used independently.

case

When more than two directions for the control of flow are needed, if clauses may be nested, but the case structure provides a more elegant way of doing this. The case structure is of the form:

case string in
pattern) command_list_1;;
pattern) command_list_2;;
       --
       --
pattern) command_list_N;;
esac

The shell attempts to match the string with each pattern in turn. When a pattern that matches string is found, the appropriate command list is executed, and the case command is then terminated.

The case command is often used to give the user a choice of options, as in the following:

% cat pick
echo "Type one of the following:"
echo " 1 -  who am I?"
echo " 2 -  who is logged on?"
echo " 3 -  date"
echo " 4 -  calendar"
read n
case $n in
  1) whoami ;;
  2) who ;;
  3) date ;;
  4) cal ;;
esac

Study the following, rather more complex, example:

% cat test.2
echo "Give me a letter:"
read letter
case $letter in
  [aeiou]) echo "That's a vowel!";;
  [b-df-hj-np-tv-z]) echo "That's a consonant!";;
  [A-Z]) echo "I said lower case!";;
  [1-9]) echo "I said a letter, not a number!";;
  *) echo "What's that?"
esac
echo "Thank you and goodbye"
% test.2
Give me a letter:
a
That's a vowel!
Thank you and goodbye
% test.2
x
That's a consonant!
Thank you and goodbye
% test.2
;
What's that?
Thank you and goodbye
%

Note that the last pattern in this case clause will match anything if a match has not already been found.

for

The for command can be used to apply a list of commands to a series of variables. It has the general form:

for variable [in wordlist]
do
command-listdone

The wordlist is a series of strings separated by spaces. The variable takes the value of each of this strings consecutively and then runs the command list. Here is an example:

for n in one two three four five six seven
do
  echo $n
done

This script will output the list of words ('one', 'two', etc.)

while

The while command allows a sequence of commands to be executed repeatedly while certain conditions are met. It takes the form:

while command_list_1do
command_list_2done

If command_list_1 is exited successfully, then command_list_2 is executed. This process continues until command_list_1 fails. Here is an example:

flag=y
while test $flag = y
do
echo Do it again?
read flag
done

The loop will be repeated while the value of the variable flag remains 'y'.

until

The until command tests for the opposite condition to the while command. Command-list-1 is executed until command_list_2 fails. The following will do the same as the example with while above:

flag=y
until test $flag = n
do
echo Do it again?
read flag
done


Exercises

1. Write a script called hello which outputs the following:


your username

the time and date

who is logged on

Also output a line of asterices (*********) after each section.

2. Put the command hello into your .login file so that the script is executed every time that you log on.


3. Write a script that will count the number of files in each of your subdirectories.


[Next Chapter] [Top page] [Previous Chapter]