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.
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.
Now we have created the file, we can display it:
$ more newcmd
date
pwd
ls
$
As you can see, it contains three Unix commands, one per line.
We can now execute the commands in
newcmd
by entering this command:
$ sh newcmd # do NOT do this Mon May 23 11:25:42 BST 1994 /homedir/cms/ps/book.unix chapter1 chapter2 chapter3 chapter4 newcmd $
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:
$ sh < newcmd # do NOT do this Mon May 23 14:41:35 BST 1994 /homedir/cms/ps/book.unix chapter1 chapter2 chapter3 chapter4 newcmd $
In this case, the alternative is longer and we will not
use it.
The point is that
sh
is not totally different to the other commands; it can
get its input from anywhere - even from a pipe.
There is an alternative way to execute the commands in
newcmd
-
here it is:
$ . newcmd # do NOT do this Mon May 23 13:18:41 BST 1994 /homedir/cms/ps/book.unix chapter1 chapter2 chapter3 chapter4 newcmd $
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.
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:
$ chmod 700 newcmd $ ls -l newcmd -rwx------ 1 cmsps 12 May 23 11:21 newcmd $ newcmd Mon May 23 13:34:53 BST 1994 /homedir/cms/ps/book.unix chapter1 chapter2 chapter3 chapter4 newcmd $ newcmd Mon May 23 13:34:55 BST 1994 /homedir/cms/ps/book.unix chapter1 chapter2 chapter3 chapter4 newcmd $
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.
This new command of ours can even have its output redirected - just like the standard Unix commands. For example:
$ newcmd > output $ more output Fri May 27 14:34:44 BST 1994 /homedir/cms/ps/book.unix chapter1 chapter2 chapter3 chapter4 newcmd output $
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.
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.
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
parameters
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.
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!
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.)
Answer
$ more q9.2 echo 'These are the files on' date ls q9.1 $ q9.2 These are the files on Mon Jul 13 21:29:14 BST 2009 flag blood 2 $
Notice that we don't duplicate the existing code from
q9.1.
That would be re-inventing the wheel and lead to maintenance
problems.
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.
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.
$ 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!
$ more q9.2 echo 'These are the files on' date ls q9.1 $ q9.2 These are the files on Mon Jul 13 21:29:14 BST 2009 flag blood 2 $
Notice that we don't duplicate the existing code from
q9.1.
That would be re-inventing the wheel and lead to maintenance
problems.
$ 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.
$ 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.
http://homepages.shu.ac.uk/~cmsps/unix/scripts.html
Last updated: Thursday 05 April 2012 at 17:45