Options Processing

Often shell scripts wish to implement command-line options as part of the shell script. Instead of manually programming an option processor, the shell offers a utility to accomplish this task correctly and portably, called getopts.

The getopts takes an option string and variable name as its first two arguments, and then a list of arguments,

getopts optstring name [arg...]

Arguments

Option String

The first argument to getopts is the option string. The option string is a list of option characters that getopts will scan for in the argument list; it takes the form,

   [":"][opt-char[":"]]...

An optional leading colon modifies how getopts behaves if it encounters an option not listed in the option string, or if it encounters an option that requires an argument, but that argument is missing. Each option character may optionally be followed by a colon, which indicates that that option requires an argument.

For example, the option string hvi:o: causes getopts to scan the provided argument list for option flags -a and -v, and for argument-taking options -i and -o.

Name

The second argument to getopts is the name of a variable. When getopts recognizes an option from the option string, the named variable is assigned that option character.

Remaining Arguments

The remaining positional arguments are those which are to be scanned by getopts for options. Typically, these are the positional arguments of a shell script or function, as in, getopts "optstring" OPTCHAR "$@"

Exit Status

An exit status of zero is returned when an option is found; otherwise, if the end of options is encountered or an error occurs, a non-zero status is returned.

Operation

getopts tracks the index of the next argument to process in the OPTIND shell variable; this allows getopts to be repeatedly executed in a loop to process option arguments. When the shell is invoked, OPTIND is initialized to 1.

When getopts encounters an option character, it sets the variable specified by name to that character. If an unknown option is encountered, the variable name is set to <question-mark> (‘?’).

Additionally, if that option takes an argument as specified in the option string with a trailing colon, the variable OPTARG is set to the value of that argument. If an argument is missing, then the variable specified by name is set to a ‘?’, OPTARG is unset, and a diagnostic message is printed to standard error; getopt still returns 0 in this case.

Leading Colon in Option String

As mentioned above, the option string may include an optional leading colon as its first character. When this is included, it affects how unknown options and missing arguments are handled and suppresses any diagnostic messages that getopts would normally print.

If an unknown option is encountered, the variable specified by name is still set to ‘?’, but OPTARG is additionally set to that character so that it may be inspected.

In the event of an option missing a required argument, getopts will instead assign the variable specified by name to ‘:’, and set OPTARG to the character representing that option, so that it may be inspected.

End of Options

If the end of options is encountered, getopts returns a non-zero exit status and set OPTIND to the index of the first non-option argument; if there are none, it is set to $# + 1. The variable specified by name is set to ‘?’.

Along with reaching the end of its arguments, when getopts encounters an argument which does not begin with ‘-’, or which is exactly “–”, it treats this as the end of options (and the beginning of non-option arguments).

Multiple Invocations

If getopts was used previously, it must be called with exactly the same arguments as before–same option string, variable name, and arguments. To call getopts with different arguments, OPTIND must first be reset to 1.

Calling getopts with different arguments without first resetting OPTIND, or changing OPTIND to any value besides 1 produces undefined results.

Usage

In general, the leading colon should always be included in the option string, since it provides additional information to the caller and suppresses uncontrollable diagnostic messages for missing arguments.

getopts is typically called in a while loop with a case clause based on the value of the option character.

Here is a simple example,

options.sh

print_usage() {
  printf 'Usage: %s [-hv] [-i input_file] [-o output_file] ...\n' "$0"
} >&2

print_help() {
  print_usage
  printf '%s\n' \
  '-h              Show this help' \
  '-v              Show version information' \
  '-i input_file   Set input filename' \
  '-o output_file  Set output filename'
} >&2

print_version() {
  printf '%s - version 1.0\n' "$0"
} >&2

# Process options
while getopts ':hvi:o:' OPTCHR "$@"
do
  case "$OPTCHR" in
  i) IN_FILE=$OPTARG;;
  o) OUT_FILE=$OPTARG;;
  h) print_help
     exit 0;;
  v) print_version
     exit 0;;
  :) printf '%s: option requires an argument --%c\n' "$0" "$OPTARG" >&2
     exit 1;;
  \?) printf '%s: illegal option -- %s\n' "$0" "$OPTARG" >&2
     print_usage
     exit 1;;
  esac
done

# Remove options from positional parameters
shift $((OPTIND-1))

# If the first non-option is "--", get rid of it
if [ "$1" = "--"]
then
  shift
fi

# Here are the remaining positional parameters
if [ "$#" -ne 0 ]
then
  printf 'Remaining arguments: %s\n' "$*"
fi

Demonstration

$ sh options.sh -h
Usage: options.sh [-hv] [-i input_file] [-o output_file] ...
-h              Show this help
-v              Show version information
-i input_file   Set input filename
-o output_file  Set output filename
$ sh options.sh -v
options.sh - version 1.0
$ sh options.sh -i input -o output arg1 arg2
Remaining arguments: arg1 arg2
$ sh options.sh -x
options.sh: illegal option -- x
$ sh options.sh -i
options.sh: option requires an argument -- x