So far, our scripts have only worked with fixed filenames. In this chapter we learn to make them more flexible.
So far, we have referred to the things on the command line after a Unix command as arguments. Now, we are going to look at them from inside the script. From that viewpoint, they are called parameters. I use both terms because I am a bit pedantic. Sometimes, it is difficult to know which viewpoint to use and, therefore, which term to use -- as you will see.
Suppose we wanted a command to make a back-up copy of a file. We could write a script:
$ cat > bu cp precious precious.bu ^D $ chmod 700 bu $
Unfortunately, it only works with the one file called precious! We need something a little more flexible. This is where parameters come in:
$ cat > bu cp $1 $1.bu ^D $
$1
is called a
parameter; the
1
indicates that
it is actually the first (and only) parameter the script
is expecting.
Notice that
$1
occurs twice in this example.
Now if we want to back-up the file called precious and check what happened, we enter:
$ bu precious $ ls p* precious precious.bu $
If we want to preserve
bu
itself, we enter:
$ bu bu
$
What has happened is that shell replaced
every occurrence of
$1
with the first word
on the command line after the name of the command.
In both examples, the name of the command is
bu
.
In the first example the word after
bu
is
precious
so
shell replaces both occurrences of
$1
in:
cp $1 $1.bu
to get:
cp precious precious.bu
and then executes that
cp
command.
In the second example, the
cp
command is effectively:
cp bu bu.bu
Our
bu
command is now much more useful: it can be used with any
file, no matter what it is called.
If we forget to give the name of a file to the
bu
command, shell
has literally nothing to put in place of
$1
and that
is what it does.
Entering this command:
$ bu
causes shell to try to execute this copy command:
cp .bu
Notice that there are two spaces between the words.
That is because there was one before and one after the first
missing
$1
.
Fortunately, the command is illegal and
cp
displays an error message:
$ bu
Usage: cp [-ip] f1 f2; or: cp [-ipr] f1 ... fn d2
$
The error message is rather confusing.
Right now, we can remember that
bu
uses
cp
but in a week or so we may have forgotten.
And if we give
bu
to someone else they will certainly be baffled.
Obviously it would be better for
bu
to check that it had the
right number of arguments.
We will do that in a later chapter
when we have learned how scripts
select alternative actions.
Just as with missing arguments, shell does not check for extra
arguments; they are simply ignored.
This example shows that giving two file-names to
bu
only causes one back-up to be made:
$ bu precious priceless $ ls p* precious precious.bu $
So far, our scripts have only displayed anything
by executing the Unix commands that gave the desired output.
For example,
newcmd
displayed the name of the current directory
by executing the
pwd
command.
In that sense, we have been choosing "off the peg" output so far.
Often however, we need to display some text specific to what
we are doing; we need "made to measure" output.
As an example, what if we want
bu
to display a comforting message to confirm
the back-up has been made?
There is no Unix command designed specially to do that,
but there is a command designed to do tailored output
in scripts.
That is, in fact, the real purpose of the seemingly useless
echo
command that we saw in Chapter ??.
This version of
bu
uses
echo
to confirm that a back-up has
been done:
$ more bu cp $1 $1.bu echo $1 backed-up in $1.bu $ bu precious precious backed-up in precious.bu $
Unfortunately, the message is always displayed, even if no back-up was actually done, as in this example:
$ bu nonesuch
cp: nonesuch: No such file or directory
nonesuch backed-up in nonesuch.bu
$
Again, this will be improved later.
Shell allows us to refer to up to nine parameters using
$1
through to
$9
.
Inside a script, we can refer to the parameters in any order.
For instance,
we can use
$2
before
$1
Also we can use each parameter as many times as we like.
In the last version of
bu
we used
$1
four times.
Shell provides three facilities to do with parameters. This little script demonstrates them all:
$ cat > params echo this script is called $0 echo it has $# parameters echo here they all are: $* ^D $ chmod 700 params $ params first second third fourth this script is called params it has 4 parameters here they all are: first second third fourth $
The new features are
$0
,
$#
and
$*
.
You can probably work out what they do from the example.
$0
is the name the script was called by;
that is, the first word on the command line.
This means that scripts can put their correct name in error messages
even if they have been renamed or called via an alias of some sort.
$#
is the number of parameters.
$*
is an abbreviation for all the parameters.
In the example
there were extra spaces between the arguments
in the command to execute
params
.
However,
$*
means all the parameters with
one
space between them.
The result is that our extra spaces are dropped.
Spaces are used to separate arguments.
As we have seen, extra spaces before or after an argument
are ignored.
This means that we cannot easily have arguments containing spaces.
What we have to do is use quotation marks around
the arguments containing spaces.
Executing
params
again, shows the effect:
$ params first 'second third' fourth
this script is called params
it has 3 parameters
here they all are: first second third fourth
$
As you can see, there are only three arguments this time; the second one contains two words and ten spaces.
You can have as many parameters as you like - you just
can't refer to more than nine directly!
Shell's
shift
command allows you to discard lower-numbered parameters to "make way"
for the inaccessible higher-numbered ones.
This script uses the
shift
command:
$ more many echo Originally $# parameters', $1 was' $1 shift 4 echo Now $# parameters', $1 is' $1 $ many a b c d e f g h i j k l m Originally 13 parameters, $1 was a Now 9 parameters, $1 is e $
The
shift 4
throws away the values in parameters one to four
and re-numbers the remaining ones.
$0
is not affected.
If you don't say how many parameters to
shift
shell
shift
s once.
The previous example showed
shift
being used to allow us to refer directly to the higher
numbered parameters.
It isn't normally used for that.
As we will see later, that is best done using shell's
for
statement.
Often however, the first few arguments to a command are different to the rest.
In such a case, we handle the special arguments at the start of the command
line and then use
shift
to clear out them out of the way before dealing with the
ordinary, remaining arguments.
For example, suppose we have a script which transfers files to another computer using FTP (the file transfer protocol). It's first two arguments might be the name of other computer and the name of the directory on the remote computer. The third and subsequent arguments would be the list of files to be transferred into the remote directory. Thus, the command line might look something like this:
$ FTPput apple.shu.ac.uk Unix f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 \ > f11 f12 f13 f14 f15 f16 f17 $
As you can see, there are nineteen arguments of which seventeen make up a simple list of files.
The way to deal with this is to process the first two parameters,
shift them out of the way,
and then process the remaining ones using a
for
loop.
You probably won't be able to follow the code fully until we have covered
variables in the following chapter and the
for
statement in Chapter ??.
However, here are the first four lines:
$ head -4 FTPput
computer=$1
directory=$2
shift 2
for file
$
The first two lines copy the special parameters into variables.
Then the
shift
statement discards the two special parameters so that the remaining ones
can be processed by a simple loop.
Note that this technique can deal with
hundreds
of arguments.
For each of the following, you have to write a shell script. Name them "q10.1" ...
Write a shell script to list the directory specified (by the user) under a suitable heading. For example:
$ q10.1 bin
would do the
bin
directory.
("List the directory", means output a list of the things in the directory.)
Answer
$ more q10.1 echo Listing of $1: ls $1 $ q10.1 red Listing of red: blood flag $
Hide
The
banner
command displays a word in large letters.
Write a shell script to display three (user specified) large words, one
after another down the screen with a
banner
of "-----" between the words.
That is, you should
banner
five things altogether.
Answer
$ more q10.2 banner $1 banner ----- banner $2 banner ----- banner $3 $ q10.2 small is fine #### # # ## # # # ## ## # # # # #### # ## # # # # # # # # ###### # # # # # # # # # # #### # # # # ###### ###### ##### ##### ##### ##### ##### # #### # # # #### # # # # # # #### ##### ##### ##### ##### ##### ###### # # # ###### # # ## # # ##### # # # # ##### # # # # # # # # # ## # # # # # ###### $
Hide
Write a shell script to display its number of arguments followed by all but the first two of them.
Answer
$ more q10.3 echo $# shift 2 echo $* $ q10.3 f1 f2 f3 f4 2 f3 f4 $
Hide
Change one character in the script from question three to make it display its name instead of the number of arguments.
Answer
$ more q10.4 echo $0 shift 2 echo $* $ q10.4 f1 f2 f3 f4 q10.4 f3 f4 $
Hide
In each of the answers the script is displayed using
more
; then it is executed by typing its name.
$ more q10.1 echo Listing of $1: ls $1 $ q10.1 red Listing of red: blood flag $
$ more q10.2 banner $1 banner ----- banner $2 banner ----- banner $3 $ q10.2 small is fine #### # # ## # # # ## ## # # # # #### # ## # # # # # # # # ###### # # # # # # # # # # #### # # # # ###### ###### ##### ##### ##### ##### ##### # #### # # # #### # # # # # # #### ##### ##### ##### ##### ##### ###### # # # ###### # # ## # # ##### # # # # ##### # # # # # # # # # ## # # # # # ###### $
$ more q10.3 echo $# shift 2 echo $* $ q10.3 f1 f2 f3 f4 2 f3 f4 $
$ more q10.4 echo $0 shift 2 echo $* $ q10.4 f1 f2 f3 f4 q10.4 f3 f4 $