Bash getopts builtin command

Updated: 02/01/2021 by Computer Hope
getopts command

On Unix-like operating systems, getopts is a builtin command of the Bash shell. It parses command options and arguments, such as those passed to a shell script.

Description

getopts is the bash version of another system tool, getopt. Notice that the bash command has an s at the end, to differentiate it from the system command.

While the getopt system tool can vary from system to system, bash getopts is defined by the POSIX standard. So if you write a script using getopts, you can be sure that it runs on any system running bash in POSIX mode (e.g., set -o posix).

getopts parses short options, which are a single dash ("-") and a letter or digit. Examples of short options are -2, -d, and -D. It can also parse short options in combination, for instance -2dD.

However, getopts cannot parse options with long names. If you want options like --verbose or --help, use getopt instead.

Syntax

getopts optstring optname [ arg ]

How it works

getopts processes the positional parameters of the parent command. In bash, this is stored in the shell variable "$@". So, if you run this command:

mycmd -a argument1 -b argument2

During the time that mycmd is running, the variable $@ contains the string "-a argument1 -b argument2". You can use getopts to parse this string for options and arguments.

Every time you run getopts, it looks for one of the options defined in optstring. If it finds one, it places the option letter in a variable named optname. If the option does not match those defined in optstring, getopts sets variable optname to a question mark ("?").

If the option is expecting an argument, getopts gets that argument, and places it in $OPTARG. If an expected argument is not found, the variable optname is set to a colon (":"). Getopts then increments the positional index, $OPTIND, that indicates the next option to be processed.

The special option of two dashes ("--") is interpreted by getopts as the end of options.

getopts is designed to run multiple times in your script, in a loop, for example. It processes one option per loop iteration. When there are no more options to be processed, getopts returns false, which automatically terminates a while loop. For this reason, getopts and while are frequently used together.

Specifying the optstring

optstring is a string which defines what options and arguments getopts look for. For instance, in this call to getopts:

getopts "apZ" optname

The options expected by getopts are -a, -p, and -Z, with no arguments. These options can be combined in any order as -aZ, -pa, -Zap, etc.

Let's say that you'd like the -a and -Z options to take arguments. You can specify this by putting a colon (":") after that option in optstring. For example:

getopts "a:pZ:" optname

Now you can specify arguments to the -a and -Z options such as -a argument1 -pZ argument2. The -p option cannot take arguments, because there is no colon after the p in optstring.

Note

There are two reserved characters which cannot be used as options: the colon (":") and the question mark ("?").

Verbose error checking

By default, getopts will report a verbose error if it finds an unknown option or a misplaced argument. It also sets the value of optname to a question mark ("?"). It does not assign a value to $OPTARG.

If the option is valid but an expected argument is not found, optname is set to "?", $OPTARG is unset, and a verbose error message is printed.

Silent error checking

However, if you put a colon at the beginning of the optstring, getopts runs in "silent error checking mode." It will not report any verbose errors about options or arguments, and you need to perform error checking in your script.

In silent mode, if an option is unexpected, getopts sets optname to "?" and $OPTARG to the unknown option character.

If the option is OK but an expected argument is not found, optname is set to a colon (":") and $OPTARG is set to the unknown option character.

Specifying custom arguments

You will usually want getopts to process the arguments in $@, but in some cases, you may want to manually provide arguments for getopts to parse. If so, you can specify these args as the final argument of the getopts command.

Examples

Here is a bash script using getopts. The script prints a greeting, with an optional name, a variable number of times. It takes two possible options: -n NAME and -t TIMES.

#!/bin/bash
NAME=""                                   # Name of person to greet.
TIMES=1                                   # Number of greetings to give. 
usage() {                                 # Function: Print a help message.
  echo "Usage: $0 [ -n NAME ] [ -t TIMES ]" 1>&2 
}
exit_abnormal() {                         # Function: Exit with error.
  usage
  exit 1
}
while getopts ":n:t:" options; do         # Loop: Get the next option;
                                          # use silent error checking;
                                          # options n and t take arguments.
  case "${options}" in                    # 
    n)                                    # If the option is n,
      NAME=${OPTARG}                      # set $NAME to specified value.
      ;;
    t)                                    # If the option is t,
      TIMES=${OPTARG}                     # Set $TIMES to specified value.
      re_isanum='^[0-9]+$'                # Regex: match whole numbers only
      if ! [[ $TIMES =~ $re_isanum ]] ; then   # if $TIMES not whole:
        echo "Error: TIMES must be a positive, whole number."
        exit_abnormal
      elif [ $TIMES -eq "0" ]; then       # If it's zero:
        echo "Error: TIMES must be greater than zero."
        exit_abnormal                     # Exit abnormally.
      fi
      ;;
    :)                                    # If expected argument omitted:
      echo "Error: -${OPTARG} requires an argument."
      exit_abnormal                       # Exit abnormally.
      ;;
    *)                                    # If unknown (any other) option:
      exit_abnormal                       # Exit abnormally.
      ;;
  esac
done
if [ "$NAME" = "" ]; then                 # If $NAME is an empty string,
  STRING="Hi!"                            # our greeting is just "Hi!"
else                                      # Otherwise,
  STRING="Hi, $NAME!"                     # it is "Hi, (name)!"
fi
COUNT=1                                   # A counter.
while [ $COUNT -le $TIMES ]; do           # While counter is less than
                                          # or equal to $TIMES,
  echo $STRING                            # print a greeting,
  let COUNT+=1                            # then increment the counter.
done
exit 0                                    # Exit normally.

If this script is named greeting, here's what the output looks like with different options:

./greeting
Hi!
./greeting -n Dave
Hi, Dave!
./greeting -t 3
Hi!
Hi!
Hi!
./greeting -t 4 -n Betty
Hi, Betty!
Hi, Betty!
Hi, Betty!
Hi, Betty!
./greeting -n
Error: -n requires an argument.
Usage: ./greeting [ -n NAME ] [ -t TIMES ]
./greeting -t
Error: -t requires an argument.
Usage: ./greeting [ -n NAME ] [ -t TIMES ]
./greeting -t -1
Error: TIMES must be a positive, whole number.
Usage: ./greeting [ -n NAME ] [ -t TIMES ]
./greeting -t 0
Error: TIMES must be greater than zero.
Usage: ./greeting [ -n NAME ] [ -t TIMES ]

while — Execute a set of actions while a certain condition is true.