Chapter 14

Shell Scripts

Introduction

So far, we have only seen examples where Unix commands were simply
typed at the terminal in response to the
$
prompt.
In this chapter we will see examples where the Unix commands are
stored in a file and are executed again and again without our
having to re-type them.
When we have put some commands into a file, we can use the name
of the file as a new command.
This means that we can create our own commands which work
like the built-in
commands we saw in earlier chapters.
An executable file containing Unix commands is
called a
shell script, or simply, a
script.
Shell scripts are one of the most powerful and useful facilities
of Unix.

Our very first script

This command puts some text into a file called
newcmd:

$ cat > newcmd # do NOT do this
date
pwd
ls
^D
$

Putting text in a file is best done with
vi
or one of the other
Unix editors.
However, for
very
small files, using
cat
and output
redirection is simpler.
Also, for the purposes of this chapter, it is easier to follow.

Notice that the three commands are executed in the order in which
they occur in the file.
Also,
note that the output from the three commands is joined together;
unless we know what output to expect, we cannot tell which lines
are from which command.
In fact,
the first line of output is from the
date
command, the second
is from
pwd
and the remainder are from
ls.

The
sh newcmd
command is unfamiliar but not all that strange.
As we saw in Chapter ??,
sh
is the name of the shell program -
the program that processes the commands
we type in at the keyboard.
So our command makes
Unix run
sh
and tells
sh
to process the commands in the
newcmd
file.
If we had forgotten to put the name of a file after
sh,
the shell would have tried to get its commands from the keyboard -
which is what it usually does.

You might ask: if
sh
is already reading our commands from the
keyboard, why do we have to start it?
The answer is: we are starting
another copy
of
sh
which
is quite separate from the one that is processing our keyboard
commands.
We need to give the two copies names to distinguish between them.
We will call the shell
that is accepting commands
from the keyboard the
interactive
shell;
we will call the other the
non-interactive
shell.
The connection between them is that the interactive
shell starts off the non-interactive shell and
waits for it to finish.
When the non-interactive shell gets to the end of the
newcmd
file, it terminates.
This is what the interactive shell has been waiting for; it displays
the
$
prompt and waits for the next command to be entered
at the keyboard.

As you should by now expect with Unix, you can use redirected input
instead of, or as well as, a file name.
For example, we could use:

The dot is a command that tells the shell to
switch to
another file and process the commands it contains.
At the end of the file, the shell reverts to getting
commands from its original source of input.
The difference between this method and the one before is that only
one shell is involved -
there is no non-interactive shell.
This difference has some quite subtle implications which we will
return to later ??.

By the way,
the space after the dot is necessary; all Unix commands have white space
between them and what follows.

Look no command

It would be tedious to have to keep typing either
sh
or a dot
in front of the file-name every time we wanted to execute the commands
in a file.
Fortunately we don't have to.
If we make the file executable, Unix will
automatically
start a non-interactive shell to process the commands in the file
whenever we use the name of the file as if it were a command.
For example:

In the example, the
chmod
command makes the file executable and
the
ls -l
checks that we did it properly.
Next, the
newcmd
file is executed
twice.
The reason for executing it
twice
is to show that the
chmod
only has to be done
once ever.
The script can be edited and Unix will not "forget" that it was
made executable.

Now that we can use the filename on its own,
newcmd
really
does look like a new command.
This is the main purpose of shell scripts.

Our script is just like the other commands

This new command of ours can even have its output redirected -
just like the standard Unix commands.
For example:

Similarly, we can send the output from
newcmd
via a pipe
to another command.
In this example the output is sent to the
wc
command:

$ newcmd | wc
8 13 105
$

Later, we will see that we can redirect
a script's input as well as its output.

Our script seems bashful!

There is one apparent difference between our new command and
the standard ones.
We can only execute
newcmd
if it is in our working directory, as this shows:

$ mkdir elsewhere
$ cd elsewhere
$ newcmd
newcmd: not found
$ cd ..
$

Shell appears only to look for
newcmd
in the current directory.
Since
newcmd
is in the directory above
elsewhere, it is not found.

This is not much of a problem yet, but it would be, if
we tried to write scripts that changed to other directories.
We will see how to cure this little problem in a later chapter.

Shell is a complete programming language

Readers who have already learned a programming language
may like to know that all the facilities you would expect to find
in a programming language exist in the shell language.
The difference between a shell script and an ordinary program is
that:

a program contains instructions for the computer's CPU to execute
but a script contains instructions for Unix to execute;

a program's smallest step is one statement but
a script's smallest step is one Unix command.

Programs and scripts both provide the following facilities:

sequences of commands

a means of switching from one file to another

arguments

global variables

local variables

selection control structures

repetition control structures

procedures

recursion

a means of output

a means of input

comments

white space - blank lines and indentation

Writing and running the
newcmd
script has already demonstrated two of these facilities.
First, it contains a sequence of commands which are executed one
after another.
Second, the dot command makes the shell
switch from one file to another -
rather like
#include
in the C programming language.

QUESTIONS

Many of the questions in this tutorial depend on you remembering (or
digging out) the answers to simple problems concerning tool-building
and pipes.
For most of the following questions , you have to write a shell script.
Name them "q9.1" etc.

Create a shell script to output a single number that is the number
of things in your current directory Do not worry whether
the "things" are files or directories.

Answer

$ more q9.1
ls | wc -l
$ q9.1
42
$

This is a useful script to have if it took you a long time to work
out how to do it. You have encapsulated the knowledge for later
use. Of course, remembering the name of the script might be a
bigger problem!

Hide

Create a shell script to give a title, followed by: the date, a
listing of the directory, and the number from question one. (By
listing of the directory we mean a list of the things in the
directory.)

Notice that we don't duplicate the existing code from
q9.1.
That would be re-inventing the wheel and lead to maintenance
problems.

Hide

Without changing "q9.2" or creating a new script, put the output
from (2) into a file.

Answer

$ q9.2 > file
$

Notice we didn't have to write a script for this; we simply did
what you would do with any Unix command.

Hide

Create a shell script to count the directories in the current
directory.

Answer

$ more q9.4
ls -l | grep '^d' | wc -l
$ q9.4
2
$

Again we have encapsulated something we might have had to work
out so that we can simply re-run it.

Hide

ANSWERS

$ more q9.1
ls | wc -l
$ q9.1
42
$

This is a useful script to have if it took you a long time to work
out how to do it. You have encapsulated the knowledge for later
use. Of course, remembering the name of the script might be a
bigger problem!