The C shell (and tcsh) offers a shell programming language. However, as all Unix systems have the original Bourne shell available, all the script examples in this section have been written using the Bourne shell to interpret. The Bourne shell programming language is also commonly thought to be more straightforward for many tasks. All the examples in this section have been rewritten using the C shell programming and included in Appendix 3.
Each shell language has its own features, advantages and disadvantages and ease of use. The shell you decide to use for scripts will depend on personal preference and what is available to you.
Help: For more information see sh(1). For a useful Internet source of help on Bourne Shell programming see the section on WWW sites.
To create a file, the output of cat can be
redirected. Type the commands, then end with <Ctrl/d>,
or any editor can be used to create the script. The editor
used on this course is the ubiquitous emacs
editor.
All scripts should begin with a # on the first
line otherwise a Bourne shell will be invoked. It is possible to state
explicitly which executable (i.e. which shell) should execute a script
by placing ! program name directly after the
#, for example #! /bin/sh would load a
Bourne shell, #! /bin/csh would load a csh.
After the first line any other lines that begin with
# are ignored and are therefore used as a way of adding
comments to shell scripts. Here is a simple example of a Bourne shell
script for the earlier howmany example.
#! /bin/sh
# Bourne Shell Script - to display number of
current logins
echo -n "Users logged in :" ; who | wc -l
When you execute a shell script, a new shell comes into existence for its duration, disappearing when execution terminates. It is therefore not possible to make lasting changes to shell variables using an ordinary shell script. It is not necessary to remove shell variables used in shell scripts as they will be local to the shell that runs the script.
Here is an example Bourne shell script containing variables with the resultant output in bold.
#!/bin/sh
a=hello
b="hello world"
echo $a
echo $b
hello
hello world
a is a standard shell variable, b
is a string variable. It is possible to access the value of a variable
by preceding it with $.
When assigning values to variables there must be no spaces
around the equals sign (=).
For Bourne Shell programming, use of expr can be
made for arithmetical operations. expr evaluates an
expression and writes the result to the standard output stream. To
actually assign the value of an arithmetic instruction to a variable,
back quotes are required:
i=7
j=13
k=`expr $i + $j`
echo $k
20
Without use of expr, for example,
k=a+b sets k equal to the string
a+b and k=$a+$b, sets k to the
string 7+13. The spacing is important: each term being
evaluated by expr must have a space surrounding it and
the assignment to k must have no spaces around the equals
sign.
Note also that without the back quotes, ie. k= expr $a
+ $b would simply result in 20 being written to
standard output but k would not hold the new value. The
back-quotes (`) mean "do this command, substitute the
result here".
Help: For more information see expr(1).
Any arguments that are specified in a command line are
accessible within a shell script. You can visualise the command as a
subroutine with parameters. Suppose we had a shell script called
Rename, used to rename a file. The script would need to
take two arguments: the original filename and the new filename.
Rename poem six-verses
The shell script might look something like this:
#! /bin/sh
# Rename sh script
# Lines starting with # are comments
mv $1 $2
echo "$1 renamed as $2. The Unix Rename command is mv"
$1 and $2 are the first and second
positional parameters of this shell script. In this example
$1 will store the string poem and $2 will be
six-verses.
Argument, Meaning
$0$1$2$3 for
3rd etc.)$*$$As well as being able to redirect the input and output of
commands, the shell can also execute a command and make its result
available as an argument to another command. For example, suppose we
want to find out on which port tina is logged in. We
could use
who | grep tina
tina ttyp3 Sep 17 13:58 (OXVAXC)
To isolate the port, we could bring in awk to
extract the second field of the output:
who | grep tina | awk ' { print $2 } '
ttyp3
This result could be assigned to a variable,
WHICHPORT, as follows:
WHICHPORT=`who | grep tina | awk ' { print $2 } '`
The back-quotes (`) mean "do this command,
substitute the result here".
All of this could be combined in a shell script finduser which takes a username as its argument:
#! /bin/sh
# sh script to rename files
WHICHPORT=`who | grep $1 | awk ' {print $2}'`
echo $1 is logged in from $WHICHPORT
Note that the $1 refers to the first positional
argument given to the script and $2 means the second
field of output given to awk.
When a shell script is invoked, a shell is started and the script is run in that shell. This happens automatically. The same effect can be achieved by typing the name of the desired shell and then the command name (and any arguments required):
% sh scriptname arguments
It is possible to debug a script by adding the
-x flag to the shell executable:
% sh -x scriptname arguments
The -x option has the effect that commands are
echoed prior to execution.
The relational operators used depend on the command used.
For use with test, the operators =,
!=, <, >,
<= and >= are used when comparing
strings. If two integers are being compared then the following options
can be used.
-eq
-ne-lt-gt-le-ge-a
-oFor expr the following are used (angle brackets
must be preceded by a back-slash to escape the normal meaning of angle
brackets).
=!=\<\>\<=\>=Help: For more information see expr(1) and
test(1). See the following sections on the if statement and File
Operations for examples of use of test.
if CommandThe if command takes the general form:
if test condition
then
commands to do if condition returns true
else
commands to do if condition returns false
fi
For example:
if test `who | wc -l` -ge 1
then
echo Users still logged in. Do not shut down.
else
echo All clear to shut down.
fi
and
#! /bin/sh
# sh script that returns a message depending on the date
month=$1
day=$2
if test $month = April -a $day -eq 1
then
echo You\'ll receive twice your salary this month\!\!
else
echo Good Morning
fi
Using the if with test, filenames
can be tested for the following:
-d filenamefilename exists and is a
directory-e filenamefilename exists-f filenamefilename is a text file-r filenamefilename is readable-w filenamefilename is writable-x filenamefilename is executable-s filenamefilename is not
emptyFor example, the following shell script,
listdir, will list a directory, but first will test to
see if the given file is a directory.
#!/bin/sh
# Bourne Shell Script listdir - lists a directory only
file=$1
if test ! -s $file
then
echo $file does not exist.
elif test -d $file
then
ls -l $file | more
else
echo $file either does not exist or is not a directory.
fi
The command listdir progs will list all the
files in progs or print progs either does not exist or is not a
directory depending on the status of progs.
case CommandThe equivalent of the C shell switch command is
the case command.
#!/bin/sh
# Script to extract the hour from the date command
# and give the user an appropriate greeting
time=`date +"%H"`
case $time in
# The general layout
is pattern) command(s) each
separated by ;;
"13") echo It\'s lunchtime.
;;
"17") echo It\'s teatime.
;;
# For a default case value use
*) command (s)
*) echo It\'s coffee time.
;;
esac
for CommandThe for command take the general form:
for loop variable in list of items
do
body of loop statements
done
An example of creating backup files for each file in a list given to the script:
#!/bin/sh
for i in $*
do
cp $i $i.bkp
done
while CommandThe while command takes the general form:
while condition
body of while statements
end
If the initial command returns false, the body of the loop is never executed. The following example could be run in the background whilst waiting for say, an FTP (File Transfer Protocol) download to finish, ie. the while loop will test the existence of the file every 30 seconds and notify the user when it exists.
#!/bin/sh
# while example
while test ! -s $1
do
sleep 30
done
echo -n $1 exists now