CHAPTER 5
Accepting Command-Line
Options, Switches, and
Parameters

Sometimes you may want to pass optional parameters to your script, allowing its behavior to be controlled more precisely. For example, the tar archiving utility uses optional parameters. It creates archives and restores directory and file trees, but it can do so in different ways. The GNU version of tar has roughly 80 different options that can be used in various combinations to tailor the ways in which the utility performs. The major benefit of this technique is that you can write a single program and have it perform multiple tasks based on the command-line input. Additionally, you're not duplicating code by writing smaller, more specific scripts tailored to each individual task.

You are unlikely to use the code demonstrated here in its current form, as it is designed to demonstrate processing of command-line options within a framework of a specific task for a specific application environment. However, it's a good basic set of simple utilities that you could give to first-level support staff for basic initial troubleshooting so they don't have to remember all the paths and commands. That could be especially helpful if that support staff is not very proficient with the command line. To modify the sample code for more general use, you could have the code view the /var/log/messages file with one switch, perform a df -k with another switch, and perform a netstat -a with yet another. This is much like creating a set of command-line aliases to save time by reducing keystrokes for commonly used commands and utilities.

Most of the scripts I have written don't use many options because they are fairly specific in their purpose. If you need a single option, you can easily have the user supply it as a command-line parameter and check the value of $1 to see if an option was in fact passed to the script. The complexity of this technique increases dramatically when you have multiple options that can be used in combination independently of each other, and the method of evaluating command-line parameters becomes unwieldy. Also consider the difficulty of accounting for users' foibles and the ways users may specify the options—sometimes erroneously.

For instance, a typical tar command might be tar -xvf file.tar. This could also be entered as tar -x -v -f file.tar. Attempting to account for all possible combinations of user-specified options using shell-script code that works with positional variables would be very problematic.

This brings us to the getopts utility, which handles command-line switches much more elegantly. You have to concern yourself only with how the script will function based on the supplied parameters, not how to read the parameters and account for their potential variability.

The following example code does not represent a full script. It is a single function that would get sourced into your environment through a login script such as /etc/profile or through a standard library of functions (see Chapter 2). To use this function, you type its name (jkl) at the command line and pass it various parameters to perform specific tasks.

The code was used in an environment where there were multiple machines, each of which had one or more versions of the same set of applications installed. Troubleshooting problems with the active application became tedious and time-consuming because you had to begin by determining which installed version was active. The one constant was a single configuration file residing in a known location that held the active version of the installed software. The following code allows users to immediately switch to the correct configuration or log directory for quick troubleshooting:

APPHOME=/usr/local/apphome
if [ ! -f $APPHOME/myapp.sh ]
then
  echo "Myapp is not installed on this system so jkl is not functional"
  return 1
fi

First you define a variable containing the top of the directory subtree where the installed applications live; then you determine if the main configuration file exists. If it does not exist, the script should exit and provide a notification. Next comes the jkl() function itself.

jkl () {
  Usage="Usage:
   jkl [-lbmcdxh] [-f filename]
   [-h] This usage text.
   [-f filename] cat the specified file.
   [-l] Go to application log directory with ls.
   [-b] Go to application bin directory.
   [-c] Go to application config directory.
   [-m] Go to application log directory and more log file.
   [-d] Turn on debug information.
   [-x] Turn off debug information. "
  APPLOG=myapp_log
  UNAME=`uname -n`
  DATE=`date '+%y%m'`
  MYAPP_ID=$APPHOME/myapp.sh

The start of the function sets up a number of variables, the most interesting of which is Usage. The Usage variable is being formatted manually for the output of the usage statement with tabs and carriage returns. For more information on these character combinations and definitions, consult the man page for echo on your system. Here is a more readable output of the usage statement that demonstrates the formatting:

Usage:

jkl [-lf:bmcdxh]

  [-h] This usage text.

  [-f] cat specified file.

  [-l] Go to application log directory with ls.

  [-b] Go to application bin directory.

  [-c] Go to application config directory.

  [-m] Go to application log directory and more log file.

  [-d] Turn on debug information.

  [-x] Turn off debug information.

Then you define the software version numbers based on the information found in the application configuration file, as in the following code:

major=`egrep "^MAJOR_VER=" $MYAPP_ID | cut -d"=" -f2`
minor=`egrep "^MINOR_VER=" $MYAPP_ID | cut -d"=" -f2`
dot=`egrep "^DOT_VER=" $MYAPP_ID | cut -d"=" -f2`

This file isn't shown in this example, but you can assume that these values are in that file. The file is included in the downloadable script package in the Source Code/Download area of the Apress web site (www.apress.com).

The names of the various application directories are formed from the combination of application names and version-number variables. Here we assign the directory variables their values.

APPDIR=$APPHOME/myapp.$major.$minor.$dot
LOGDIR=$APPHOME/myapp.$major.$minor.$dot/log
CFGDIR=$APPHOME/myapp.$major.$minor.$dot/config
BINDIR=$APPHOME/myapp.$major.$minor.$dot/bin

Then we check to see if any command-line switches were used when the function was called. If none are found, the usage statement should be displayed. Note that the echo command uses the -e switch, which enables the use of the escape sequences found in the Usage variable.

if [ "$#" -lt 1 ]
then
  echo -e $Usage
fi

If the script did not use the -e switch, it would not format the output properly, instead printing the escape sequences along with the usage information.

User-supplied options are accessed through an argument vector, or what you may think of as an array. The getopts utility uses the OPTIND environment variable to index this array. Each time the example code function is invoked, the variable needs to be reset to 1 before option processing starts in order to point at the beginning of the options that have been passed.

OPTIND=1

As the while loop in the following code snippet iterates through the passed options, the getopts utility increments the value of the OPTIND variable and processes through any parameters that were passed.

This while loop is the core of the script. It is where the passed parameters are processed and appropriate actions are taken.

 while getopts lf:bmcdxh ARGS
 do
   case $ARGS in
     l) if [ -d $LOGDIR ] ; then
         cd $LOGDIR
         /bin/ls
       fi
     ;;
     f) FILE=$OPTARG
       if [ -f $FILE ]
       then
         cat $FILE
       else
         echo $FILE not found. Please try again.
       fi
     ;;
     b) if [ -d $BINDIR ] ; then
         cd $BINDIR
       fi
     ;;
     m) if [ -d $LOGDIR ] ; then
         cd $LOGDIR
         /bin/more $APPLOG
       fi
     ;;
     c) if [ -d $CFGDIR ] ; then
         cd $CFGDIR
       fi
     ;;
     d) set -x
     ;;
     x) set +x
     ;;
     h) echo -e $Usage
     ;;
     *) echo -e $Usage
     ;;
   esac
 done
}

The getopts command is invoked with a list of the valid switches, which it parses to determine which switches need arguments. Each time getopts is invoked, it checks whether there are still switches to be processed. If so, it retrieves the next switch (and updates the value of OPTIND), stores it in the specified environment variable (here, ARGS), and returns true. Otherwise, it returns false. In this way, the while loop iterates through the options vector. Each time the shell executes the loop body, the case statement applies the actions that the current option requires.

In this case, most of the options take you to an application-specific directory. The three most interesting cases here are the -d, -x, and -f switches. The -d switch turns on command expansion and the -x switch turns it off. This is very useful and an easy method for debugging scripts. The -f switch is different from the rest. Note that it has a colon (:) following the f in the getopts switch list. If a switch is followed by a colon, an argument should follow the switch when it is used. In our example, the -f switch lists the contents of a file and requires the filename to follow. The case branch for -f sets the FILE variable to $OPTARG. This is another special environment variable that is set by getopts to assign the argument that is passed to the switch. If the file exists, it will be displayed; if not, the code will generate an error message.

The last two switches cause the usage statement to be displayed. A more advanced example of the getopts construct can be found in Chapter 17. Additionally, I have included another script in the download package for this chapter (at www.apress.com) that performs some basic administrative tasks, including turning on and off the set -x value.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.23.79.149