Writing CSH Shell Scripts
Writing CSH Shell Scripts
Writing CSH Shell Scripts
Version 3.0
80p
Document code:
Title:
Version:
Date:
Produced by:
Guide 3
Writing C-shell scripts
3.0
June 2006
University of Durham Information Technology Service
Conventions:
In this document, the following conventions are used:
Contents
1.
Introduction........................................................................................................ 1
1.1 The aim of this course.................................................................................... 1
1.2 Before you begin............................................................................................ 1
1.3 Teaching yourself........................................................................................... 1
1.4 Further information about UNIX ...................................................................... 1
2.
3.
4.
5.
6.
7.
8.
9.
1. Introduction
1.1
1.2
1.3
Teaching yourself
This document has been written so that it can be used as a teach-yourself
guide. If you prefer to attend a Writing C-shell Scripts course, please
contact the ITS Helpdesk.
1.4
Getting started
It will be useful to create the files for this course in a new subdirectory:
1
type
cd
followed by
cd cshell
To save you from doing a lot of typing, some files for this course have
already been prepared. It will be useful to copy these files to this new
directory, so:
1
type
cp ~courses/cshell/simple/* .
Type
ls -l
in order to see which files have been copied. We will look at the contents of
each of these files as we go through the course.
3.2
Type
w
followed by
who
In your use of UNIX, the C-shell has been used to process the commands
that you type at the keyboard. It is also possible to get the shell to obey
commands given in a file. Such a file of commands is called a shell script
(or a shell procedure).
Note: The name of this file should not be the same as that of a UNIX
command. In particular, do not use the name test as there is a UNIX
command called test.
We will look at a simple example. You should have a file called spy:
1
type
cat spy
#!/bin/csh
# spy dxy3abc 920520
# spy outputs some details about what's happening on the computer.
# It takes no parameters.
w | more
echo ""
echo -n "Number of users: "
who | wc -l
The file spy contains a script. The first line of any file that is a C-shell script
should contain:
#!/bin/csh
type
csh spy
Notes: the command echo " " produces a blank line. If an n option is used
with the echo command, the parameters are sent to the standard output
without an end-of-line character.
3.3
type
cat fileinfo
The current directory has a file called fred, so in order to execute the script
on the file fred:
1
type
csh fileinfo fred
3.4
In order to be able to do this, the files containing the shell scripts must be
executable.
The file mode of a file can be changed using the chmod command.
Normally, a file that you create using an editor can only be read from or
written to:
1
type
ls -l
Notice that columns 2 to 4 of this output contain the characters rw-. In order
to make the files spy and fileinfo executable by you:
1
type
chmod u+x spy fileinfo
type
spy
followed by
fileinfo fred
Exercise A
Produce a shell script called wld which contains the commands: who, ls
and date. [Remember to include #!/bin/csh as the first line of the script.]
Check that the script works by typing the command line:
csh wld
This is because you have a shell script in your current directory called
peep.
Exercise B
Produce a shell script called lpqs which displays the contents of the printer
queues printername1 and printername2. In your shell script, use the echo
command to identify the lines of the output. Make the file executable so that
you are able to execute it just by typing. (Note: the solution given to this
exercise uses the names lasercc1 and dcc1 which do not correspond to
any of the networked printers.)
lpqs
3.5
provided that you are in the subdirectory containing the file spy. If you have
written a shell script that you may want to use from any of your
subdirectories, it is useful to put the shell script into a special subdirectory
(just containing executable commands) rather than having copies of the
shell script in each of the subdirectories where you might want to use it.
It is conventional for a user to put their private collection of shell scripts in
the directory ~/bin:
1
type
mkdir ~/bin
At this point use an editor to alter the contents of the file ~/.login. It needs
to have the following command added to the end of the file:
set path = ( $path ~/bin )
If you use the Pico editor, for example, you can alter this file by typing the
following commands:
cd
cp .login .login.old
pico .login
Make sure that you do not alter any of the existing lines of the file.
This alteration to the .login file will have no immediate effect. In order for it
to have some effect:
1
type
source ~/.login
Now make sure that you are in the directory being used for this course:
1
type
cd ~/cshell
Having done that, we ought to move shell scripts like spy and fileinfo to
the ~/bin directory:
1
type
mv spy fileinfo peep wld lpqs ~/bin
followed by
ls -l
You should find that the files spy, fileinfo, peep, wld and lpqs are no
longer in this directory. They have been moved to the ~/bin directory.
Shell scripts that have just been added to a directory that is mentioned in
the path cannot be executed immediately.
1
Type
spy
This occurs because the shell has a built-in shortcut method of getting to
such commands. And it works out the short-cuts whenever it reads the set
path command in the .login file. You can get the shell to re-initialise its
short-cuts, if you:
1
type
rehash
If you now:
1
type
spy
you should find that it executes the shell script that is in the file ~/bin/spy.
Exercise C
Produce a shell script called showbin that displays on the screen the
contents of the shell script passed as a parameter. For example, the
command:
showbin spy
Create the file showbin in the directory ~/cshell. Make the file
executable, and test it by typing:
showbin spy
If it works, type:
mv showbin ~/bin
type
cp ~courses/cshell/others/* ~/bin
We are not copying these files into the current directory (~/cshell) but into
~/bin:
2
type
ls -l ~/bin
Notice that the files that have just been copied already have the file modes
set so that we can execute them. However, since new executable files have
been added to a directory mentioned in the path, we will need to type
rehash
The file ~/bin/fileinfo2 contains an example of $*. The shell script showbin
produced in the last exercise will be used to output the contents of this file:
1
type
showbin fileinfo2
type
fileinfo2 fred bert jane
4.3
And:
cat $dir/portia.txt
is equivalent to:
cat ~courses/firstunix/portia.txt
There are some predefined shell variables. It is best not to use these
names for your own variables. A list of the shell variables that currently
have values will be displayed if you:
1
type
set
Besides the shell variables that are only active for the current shell, there
are also environment variables. These will have effect all the way from login
to logout. A list of the environment variables can be displayed:
1
type
env
Note: suppose you have files called amap, bmap, and so on, and a shell
script uses a shell variable char which contains a letter. An error will occur
if the shell script contains something like:
cat $charmap
Note: Although these notes introduce the set command as a way for a shell
script to give a value to a shell variable, you may also find it useful to type
commands like:
set dir = ~courses/firstunix
cat $dir/portia.txt
Note: introducing a shell variable that has the same value as a parameter
is often done in order to make the rest of the shell script easier to
understand.
4.4
it is often preferable to include the edit commands in the shell script. This
can be done by using what is called a here document.
Here is how it is done:
...
ed bert <<%
first line of edit commands
second line of edit commands
...
last line of edit commands
%
...
The lines between the two % characters form the here document it is
used as the standard input for the command that is given on the same line
as the <<. The line following the last line of the input must contain a % on
its own with the % appearing in the first column of the line. Note: the two %
characters may be replaced by some other suitable character or by a word.
Note also: any command or program can use a here document.
The here document may refer to parameters and variables. Here are three
worked examples:
Example 1
Suppose a shell script is required that outputs the first line of the file passed
as a parameter to the procedure. For example:
first fred
10
Solution 1
The file ~/bin/first contains a solution to this problem:
1
type
showbin first
type
first fred
means print (i.e., display on the screen) lines 2 to 7 of the file being edited.
Note that: 1,1p can be abbreviated to 1p. The q command means quit the
editor.
Example 2
Suppose a shell script is required that alters a file replacing all occurrences
of one string by some other string. The script is to be called by typing a
command like:
rao seperate separate first.tex
Type
showbin rao
11
means that the command cmd is to be performed on all lines that contain
the string str. The s command:
s/old/new/
means substitute the string new for the string old. If the string old is a null
string as in:
s//$2/gp
then the old string is the last string that was typed - in this case, it is $1. A
g at the end of an s command means change all occurrences on the line,
and a p means print each line on the screen.
Example 3
A shell script is required that indents each line of a file by 6 spaces. So:
add6 fred
is to alter the file fred so that each line of fred is indented by 6 spaces.
Solution 3
The following solution does not work:
#!/bin/csh
# add6 dxy3abc 920308
# add6 adds 6 spaces to the start of each line of a file.
# It takes one parameter which is the name of a file.
ed -s $1 <<%
1,$s/^/
/
w
q
%
12
It will fail because the shell would interpret the $s to mean use the value of
the variable s. To prevent this, use a \ to quote the $ character:
...
ed -s $1 <<%
1,\$s/^/
/
w
q
%
1,$s/^/
...
ed -s $1 <<'LastLine'
/
w
q
'LastLine'
Exercise E
The UNIX command tail can be used to output the last 10 lines of a file,
e.g.:
tail fred
Produce a shell script called last10 which does this task. [Do not cheat by
using the tail command in your script!] Your script should use ed and a
here document. [Hint: the ed command $-9,$p can be used to output the
last 10 lines of the file.] Test your script by:
last10 bert
Exercise F
Suppose you want a shell script to output some explanatory information to
the screen, say, the following 4 lines:
You are using the NIH product called 'SuperEd' on the file $1.
We hope you find this product convenient and user-friendly.
You can support us in our endeavours by sending $27 to
the following address: NIH Software Ltd., NIH Street, NIHTown.
The shell script could cat a file that contains the 4 lines.
The shell script could use 4 echo commands.
The shell script could use a cat command that gets its input from a
here document.
13
Produce a shell script called supered that takes one parameter, the name
of a file. The only task it performs is to output the above 4 lines using the
third method mentioned above.
Note that the text contains a $1. Here your script should output the filename
that is passed to supered as a parameter. The text also contains $27. Here
you should output the characters $27.
5.2
14
5.2.1
Type
showbin fileinfo3
type
fileinfo3 fred bert
When the script is executed, the shell replaces the $* with a list of the
parameters that have been passed to the script. Advice: if you are thinking
of writing a shell script to do some task on a file, turn it into one which does
the task on any number of files passed as parameters.
Note: do not use a foreach loop to execute a command which will already
loop over filenames. For example:
...
foreach filename ($*)
ls -l $filename
end
...
Exercise G
The calendar for the year 1992 can be displayed on the screen by the UNIX
command:
15
cal 1992
Produce a shell script called cals which outputs a calendar for each year
passed as a parameter to the script. For example:
cals 1992 2000 1752
5.2.2
type
showbin texfileinfo
When this script is executed, the shell will replace *.tex with a list of files
that match this pattern. Execute the script:
1
type
texfileinfo
Exercise H
Produce a shell script called zzs which makes a copy of each file in the
current directory. Each of the new filenames is to be the same as the old
filename prefixed by the characters zz. So if the directory currently contains
the files bert, fred, jane, after executing the command:
zzs
the directory will contain the files bert, fred, jane, zzbert, zzfred, and
zzjane.
16
Exercise I
Find out what happens if a shell script containing:
foreach name ($*)
is executed when the shell script has no parameters. Are the commands in
the foreach loop executed once or zero times?
Exercise J
What is the difference between the following two foreach constructs:
foreach name ($*)
foreach name (*)
5.3
Variable modifiers
A pathname, such as /home/hudson/pg/dxy3abc/papers/first.tex, is
sometimes stored in a shell variable. It can often be useful to extract the
various components of the pathname. For example, we may want the
directory part, i.e., /home/hudson/pg/dxy3abc/papers, or all of the
pathname except the extension, i.e.,
/home/hudson/pg/dxy3abc/papers/first. The C-shell has a number of
variable modifiers that can be used to extract components. The role of each
variable modifier is illustrated by the examples in the following table:
expression
$filename
$filename:r
$filename:h
$filename:t
$filename:e
value
/home/hudson/pg/dxy3abc/papers/first.tex
/home/hudson/pg/dxy3abc/papers/first
/home/hudson/pg/dxy3abc/papers
first.tex
tex
Here are some commands that make a backup copy of each .tex file that
exists in the current directory:
foreach filename (*.tex)
echo processing $filename
set root = $filename:r
cp -p $filename $root.old
end
17
6.2
The $0 notation
We have seen the use of $1, $2, ..., $9 to obtain the values of the first 9
parameters. The notation $0 refers to the name by which the shell script
was called. It is occasionally useful. [Note: there is no $argv[0] notation.]
For example, suppose the file echoall in the current directory contains a
script that includes:
echo $0 $1 $2
will produce:
echoall hi there
A variable modifier may not be used with $0. If a shell script contains:
echo $0: about to process $filename
Introduction
There are two conditional commands available in the C-shell: the if
command and the switch command. In this section, we will be considering
the if command. We will look at switches in Section 9.
18
7.2
An exit status is also returned to the shell whenever a shell script finishes.
Normally, this is the exit status of the last command that was executed by
the shell script. However, the shell script can arrange for a particular value
to be returned by using the shell's exit command. For example:
exit 2
Type
grep date ~/bin/wld
followed by
echo $status
You should find that the grep outputs the line containing the date
command, and the status variable has the value 0.
1
Type
grep freddie ~/bin/wld
followed by
echo $status
Since the file ~/bin/wld does not contain the line freddie, the grep
command produces no output, and the status variable has the value 1.
1
Type
grep date benny
followed by
echo $status
Since the file benny does not exist, you should find that the grep command
outputs an error message, and the status variable has the value 2.
19
7.4
The if command
The if command has the following syntax:
if ( expression ) then
commands
else if ( expression ) then
commands
else
commands
endif
The else if section may occur zero or more times, and the else section is
optional. Each of the expressions is evaluated in turn, and if an expression
has the value true the corresponding sequence of commands is executed
and then the command following the endif is executed.
We will look at some examples of the if command in Section 7.6.
7.5
20
7.6
condition
meaning
!b
b && c
b || c
is b false?
are b and c both true?
is at least one of b and c true?
i<j
i>j
i <= j
i >= j
i == j
i! = j
s == t
s != t
s =~p
s !~ p
-r filename
-w filename
-x filename
-e filename
-o filename
-z filename
-f filename
-d filename
21
Example 2
if ( -e $2 ) then
echo mv has not been done because $2 already exists
else
mv $1 $2
endif
Example 3
if ( (! -f $1) || -e $2 ) then
echo mv not done because $1 is not a file or $2 already exists
else
mv $1 $2
endif
Example 4
if ( ! -f $1 ) then
echo mv has not been done because $1 is not a file
else if ( -e $2 ) then
echo mv has not been done because $2 already exists
else
mv $1 $2
endif
Example 5
if ( -f $1 ) then
set fromfile = isafile
else
set fromfile = isnotafile
echo mv has not been done because $1 is not a file
endif
if ( -e $2 ) then
echo mv has not been done because $2 already exists
else if ( $fromfile == isafile ) then
mv $1 $2
endif
Exercise K
If you type:
cal 92
you will get the calendar for the year 92 rather than 1992. Produce a shell
script called nicecal that will default to the 21st century if the parameter is
less than 50 and to the 20th century if the parameter is between 50 and 99.
22
Exercise L
Produce a shell script called filetest that tests whether a file (that is passed
as a parameter) exists, is a plain file, and is readable. If the file satisfies all
these criteria, the script should execute an exit 0. Otherwise, it should
execute an exit 1.
Produce a shell script called nicecat that takes a filename as a parameter.
It should execute a filetest command, and then it should test the value of
the status variable. If the variable has the value 0, nicecat should use cat
to display the file's contents. Otherwise, it should display an error message.
Exercise M
The syntax of the UNIX command chmod is not particularly easy to
remember. Produce a shell script called plusx which adds execute
permission to each of the files passed as a parameter. If plusx is called
with no parameters, it should instead add execute permission to each of the
files in the directory ~/bin.
8.2
The $$ notation
$$ is a way of referring to the process number of the current shell. The
characters $$ are often used as part of a filename in order to generate a
unique name for a temporary file.
Suppose a shell script (called whichttys) is required that tells you the
terminal numbers of a user that is logged in. For example:
23
is to output only the lines produced by the who command that contain the
strings dxy3abc or dxy3def.
Here is one possibility for the file whichttys:
...
who >/tmp/whichttys$$
foreach username ($*)
grep $username /tmp/whichttys$$
end
rm /tmp/whichttys$$
8.3
Type
showbin raos
24
8.4
Type
showbin nicecp
25
8.5
26
Exercise N
Produce a shell script called nicerm which works through the files of the
current directory. It outputs the name of each file, reads a reply from the
standard input, and if the reply is y or Y, it removes the file. Note: whilst
testing this script, use something which would not cause a disaster, such
as:
echo would remove $filename
rather than:
rm $filename
and:
lslong
Exercise P
An example of command substitution is:
more `ls -rt`
27
and:
sizes *
type
showbin catday
28
case 1:
case 01:
set mm = 01
set som = 1
breaksw
case 2:
case 02:
set mm = 02
set som = 32
breaksw
case 3:
case 03:
set mm = 03
set som = `expr $lyf + 60`
breaksw
case 4:
case 04:
set mm = 04
set som = `expr $lyf + 91`
breaksw
case 5:
case 05:
set mm = 05
set som = `expr $lyf + 121`
breaksw
case 6:
case 06:
set mm = 06
set som = `expr $lyf + 152`
breaksw
case 7:
case 07:
set mm = 07
set som = `expr $lyf + 182`
breaksw
case 8:
case 08:
set mm = 08
set som = `expr $lyf + 213`
breaksw
case 9:
case 09:
set mm = 09
set som = `expr $lyf + 244`
breaksw
case 10:
set mm = 10
set som = `expr $lyf + 274`
breaksw
case 11:
set mm = 11
29
30
then the shell reads the file ScriptFile checking it for syntax errors. There is
no need to supply parameters, because the commands of the shell script
are not executed. If there is an error, only an error message is output - it
does not tell you which line is in error.
Note: if ScriptFile is not in the current directory, you will have to supply the
full pathname of ScriptFile, e.g.:
csh -n ~/bin/ScriptFile
If you type:
csh -nv ScriptFile
then the script is executed. Each line of the script is output before it is
executed.
The x option tells the shell to output each line after variable and command
substitutions have taken place but before the line is executed. It can be
combined with the v option:
csh -vx ScriptFile parameter ...
or:
foreach filename ($*)
echo processing the file $filename
...
31
The echo command can also be useful when testing a shell script that
could be disastrous if it goes wrong. Put echo at the start of a command
line to prevent it doing its dastardly deed, and only remove it when you are
sure it will do what you want it to do. An example is illustrated by:
foreach filename (*.tex)
set root = $filename:r
echo mv $filename $root.old
end
Finally, if you think that the earlier part of a script is failing to work properly,
then put an exit command at a suitable point to stop the shell from
executing the rest of the script. First, get the code prior to this exit
command working properly, before moving the exit command to a later
point in the script.
32