Sams Teach Yourself Shell Programming in 24 Hours
(Publisher: Macmillan Computer Publishing)
Author(s): Sriranga Veeraraghavan
ISBN: 0672314819
Publication Date: 01/01/99

Previous Table of Contents Next


Using getopts

To get a feeling for how getopts works and how to deal with options, write a script that simplifies the task of uuencoding a file.

For readers who are not familiar with uuencode, it is a program that was originally used to encode binary files (executable files) into ASCII text so that they could be emailed or transferred via FTP. Today, MIME encoding has taken the place of uuencoding for email attachments, but it is still used for posting binaries to newsgroups and transferring them via modem.

You’ll first examine the interface of this script, which makes it easier to understand the implementation.

This script should be able to accept the following options:

  -f to indicate the input filename
  -o to indicate the output filename
  -v to indicate the script should be verbose

The getopts command to implement these requirements is

getopts e:o:v OPTION

This indicates that all the options expect for –v to require an additional parameter. The variables you require in order to support this are

  VERBOSE, which stores the value of the verbose flag. By default this is false.
  INFILE, which stores the name of the input file.
  OUTFILE, which stores the name of the output filename. If this value is unset, uudecode uses the name supplied in the input file, and uuencode uses the name of the supplied input file and append to it the .uu extension.

The loop to implement the preceding requirements is as follows:

VERBOSE=false
while getopts f:o:v OPTION ;
do
    case "$OPTION" in
        f) INFILE="$OPTARG" ;;
        o) OUTFILE="$OPTARG" ;;
        v) VERBOSE=true ;;
       \?) echo "$USAGE" ;
           exit 1
           ;;
    esac
done

Now that you have dealt with option parsing, you need to deal with still other error conditions. For example, what should your script do if the input file is not specified?

The simplest answer would be to exit with an error, but with a little more work, you can make the script much more user-friendly. If you use the fact that getopts sets the variable OPTIND to the value of the last option that it scanned, you can have the script assume that the first argument after this is the input filename. If no additional arguments remain, you should exit. Your error checking consists of the following lines:

shift `echo "$OPTIND - 1" | bc`
if [ -z "$1" –a –z "$INFILE" ] ; then
    echo "ERROR: Input file was not specified."
    exit 1
fi
if [ -z "$INFILE" ] ; then INFILE="$1" ; fi

Here you use the shift command to discard the arguments given to the script by one minus the last argument processed by getopts. The exact number of arguments to shift is calculated by the bc command, which is a command line calculator. Its usage is explained in detail in Chapter 18, “Miscellaneous Tools.”

Strictly speaking, you do not have to shift the arguments. It simplifies the if statement.

After shifting the arguments, check whether the new $1 contains some value. If it does not, print and exit. Otherwise, set INFILE to the filename specified by $1.

You also need to set the output filename, in case the –o option was not specified. You can use variable substitution to accomplish this:

: ${OUTFILE:=${INFILE}.uu}

Here the name of the output file is set to the input file plus the .uu extension, if an output file is not given. Note that you use the : command to prevent the shell from trying to execute the result of the variable substitution.

When you have made sure that all the inputs are correct, the actual work is quite simple. The uuencode command that you use is:

uuencode $INFILE $INFILE > $OUTFILE ;

You should also check whether the input file is really a file before doing this command, so the actual body is

if [ -f "$INFILE" ] ; then uuencode $INFILE $INFILE > $OUTFILE ; fi

At this point the script is fully functional, but you still need to add the verbose reporting. This changes the preceding if statement to the following:

if [ -f "$INFILE" ] ; then
    if [ "$VERBOSE" = "true" ] ; then
        echo "uuencoding $INFILE to $OUTFILE... \c"
    fi
    uuencode $INFILE $INFILE > $OUTFILE ; RET=$? ;
    if [ "$VERBOSE" = "true" ] ; then
        MSG="Failed" ;
        if [ $RET -eq 0 ] ; then MSG="Done." ; fi
        echo $MSG
    fi
fi

You could simplify the verbose reporting to print a statement after the uuencode completes, but issuing two statements, one before the operation starts and one after the operation completes, is much more user-friendly. This method clearly indicates that the operation is being performed.

The complete script is as follows:

#!/bin/sh
USAGE="Usage: `basename $0` [-v] [-f] [filename] [-o] [filename]";
VERBOSE=false

while getopts f:o:v OPTION ; do
    case "$OPTION" in
        f) INFILE="$OPTARG" ;;
        o) OUTFILE="$OPTARG" ;;
        v) VERBOSE=true ;;
       \?) echo "$USAGE" ;
           exit 1
           ;;
    esac
done

shift `echo "$OPTIND - 1" | bc`
if [ -z "$1" -a -z "$INFILE" ] ; then
    echo "ERROR: Input file was not specified."
    exit 1
fi
if [ -z "$INFILE" ] ; then INFILE="$1" ; fi
: ${OUTFILE:=${INFILE}.uu}

if [ -f "$INFILE" ] ; then
    if [ "$VERBOSE" = "true" ] ; then
        echo "uuencoding $INFILE to $OUTFILE... \c"
    fi
    uuencode $INFILE $INFILE > $OUTFILE ; RET=$?
    if [ "$VERBOSE" = "true" ] ; then
        MSG="Failed" ; if [ $RET -eq 0 ] ; then MSG="Done." ; fi
        echo $MSG
    fi
fi
exit 0

With this script you can uuencode files in all of the following ways (assuming the script is called uu):

uu ch11.doc
uu –f ch11.doc
uu –f ch11.doc –o ch11.uu

In each of the preceding examples, file ch11.doc is uuencoded. The last one places the result into the file ch11.uu instead of the default ch11.doc.uu, which might be required if the document needs to be used on a DOS or Windows system.

Because this script uses getopts any of the commands given previously can run in verbose mode by simply specifying the –v option.


Previous Table of Contents Next