Main index

Introducing UNIX and Linux


More on shells

Overview
Simple arithmetic
      Arithmetic expansion
            Operators for arithmetic expansion
      The 'expr' command
Pattern matching
      Patterns
            Examples of patterns
      The case statement
Entering and leaving the shell
More about scripts with options
Symbolic links
Setting up terminals
Conventions used in UNIX file systems
Summary
Exercises

More about scripts with options

Writing a script with arguments is straightforward - you just need to examine $1, $2, etc. - but what about options?

An option is an argument that commences with a hyphen.

Suppose you wanted to write a command mycommand which, if given option -h, would print a 'help' message rather than executing:

mycommand -h
Usage: mycommand [-h]

You could check whether $1 is equal to '-h', but if you had several possible options, not just one, the number of permutations would make this a very messy programming exercise. If mycommand took option -a in addition to -h, you would have to check for:

mycommand -h
mycommand -a
mycommand -ah
mycommand -ha
mycommand -a -h
mycommand -h -a

in addition to any invalid options it might be presented with. The utility getopts is provided to assist in writing shells that have options. Consider the instance above - we could have as the contents of mycommand:

while getopts h OPTIONNAME
do
    case $OPTIONNAME in
           h)  echo 'Usage: mycommand [-h]' ;;
           ?)  echo 'Bad option to mycommand'
               exit 1 ;;
    esac
done
echo "Arguments were $@"

The action getopts performs is to look at getopts' first argument, which should be a list of letters - representing the valid options allowed by the script - and possibly colons (see below). It then looks at the next argument to the script in which it occurs. If the argument to a script is an option (i.e. preceded by a minus sign), getopts checks to see whether the option is in the list of valid options. If not, an error message is displayed. The second argument to getopts is a variable name, which is set to the option that getopts has discovered. Options must precede all other arguments that are not options. Only one option at a time will be checked, so you need to enclose getopts in a while loop. Let's see what happens when mycommand is called:

mycommand -h hello
Usage: mycommand [-h]
Arguments were -h hello
mycommand -x hello
mycommand: illegal option -- x
Bad option to mycommand
mycommand hello there
Arguments were hello there
mycommand hello -h
Arguments were hello -h

In the case of mycommand hello -h, the argument -h is not an option, since it has been preceded by an argument which is not an option.

Some commands take options that require arguments - such as lp, whose option -d must be followed by the name of the destination printer. This is handled in getopts by using colons.

If an option requires an argument, then a colon should follow the option name in the list of allowed options to getopts. When that option is encountered, the value of its argument will be stored in the system variable OPTARG. For instance, suppose a script called mymessage takes one option -m, followed by a string, and displays that string. With no arguments, mymessage displays Hello. The string would be an argument to the -m option. This script might be coded thus:

MESSAGE=Hello                 # Variable to store message
if getopts m: OPTIONNAME      # If an option found
then
   case $OPTIONNAME in        # Check which option found
       m)  MESSAGE=$OPTARG;;
       ?)  exit 1;;           # Exit if not -m
   esac
fi

echo $MESSAGE                 # Output the message

The number of the next argument to be processed by getopts is stored in OPTIND, so that by using shift you can strip off the options from the command and leave the rest of the arguments for processing later.

Worked example

Write a script mymail to call mailx to send messages. The script should take an optional argument -s (to specify the subject) and one or more other arguments to specify the recipients (just like mailx). No other options apart from -s should be allowed. If mymail is called without option -s it should prompt the user for a subject.

mymail -s "Test message" sam
(message)
mymail sam
Subject: Test message

(message)

Solution: Use getopts to process the command line the script is invoked from:

SUBJECT=""
if getopts s: OPTNAME            # Valid option is 's'
then                             # which takes an argument
   case $OPTNAME in
      s) SUBJECT="$OPTARG";;     # The argument to 's' is
                                 # SUBJECT
      ?) echo "Usage: $0 [-s subject] users"
         exit 1;;                # Exit if invalid option
   esac
fi

shift $(($OPTIND - 1))           # Remove the options
USERS="$*"                       # The rest of the line
                                 # is the recipients
if      [ -z "$USERS" ]          # ... which is compulsory
then    echo "Must specify recipients"
        exit 1                   # Exit if no recipients
fi
while   [ -z "$SUBJECT" ]        # Loop until subject
do                               # is not null
        printf "Subject (no quotes): "
        read SUBJECT
done

mailx -s "$SUBJECT" $USERS"

If you intend to write scripts which require options, then using getopts is the preferred method.


Copyright © 2002 Mike Joy, Stephen Jarvis and Michael Luck