Chapter 15

Shell Scripts - Arguments and parameters

Introduction

So far, our scripts have only worked with fixed filenames. In this chapter we learn to make them more flexible.

Arguments and parameters

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.

Parameters

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.

Missing arguments

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.

Extra arguments

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
$

Made to measure output

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.

Nine parameters

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.

Facilities concerning parameters

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.

Arguments containing spaces

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.

More than nine parameters

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 shifts once.

When to use shift

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.

QUESTIONS

For each of the following, you have to write a shell script. Name them "q10.1" ...

  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

  2. 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

  3. 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

  4. 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

ANSWERS

In each of the answers the script is displayed using more; then it is executed by typing its name.

  1. $ more q10.1
    echo Listing of $1:
    ls $1
    $ q10.1 red
    Listing of red:
    blood  flag
    $
    
  2. 
    $ more q10.2
    banner $1
    banner -----
    banner $2
    banner -----
    banner $3
    $ q10.2 small is fine
    
      ####   #    #    ##    #       #
     #       ##  ##   #  #   #       #
      ####   # ## #  #    #  #       #
          #  #    #  ######  #       #
     #    #  #    #  #    #  #       #
      ####   #    #  #    #  ######  ######
    
    
    
    
     #####   #####   #####   #####   #####
    
    
    
    
    
        #     ####
        #    #
        #     ####
        #         #
        #    #    #
        #     ####
    
    
    
    
     #####   #####   #####   #####   #####
    
    
    
    
    
     ######     #    #    #  ######
     #          #    ##   #  #
     #####      #    # #  #  #####
     #          #    #  # #  #
     #          #    #   ##  #
     #          #    #    #  ######
    
    $
    
  3. $ more q10.3
    echo $#
    shift 2
    echo $*
    $ q10.3 f1 f2 f3 f4
    2
    f3 f4
    $
    
  4. $ more q10.4
    echo $0
    shift 2
    echo $*
    $ q10.4 f1 f2 f3 f4
    q10.4
    f3 f4
    $