What is getopts statement in Shell Scripts

Processing Script Options With the getopts Statement

Options or switches are single-letter characters preceded by a + sign or a - sign. By convention, a - sign preceding a character means to turn some flag on, while a + sign means to turn some flag off. The Bourne shell supports only script options with a - sign preceding the character.

The syntax of getopts is:

$ getopts options [opt_args] var

where,

  • options represents the valid single-letter characters the script expects as options from the command line. If any single-letter character in options is followed by a colon (:), then the shell expects that option to have an argument.
  • var represents any variable name whose value is an option from the command line if the option is one of the valid single-letter characters specified in the getopts statement.

The getopts statement does not always process all options on the command line. It quits processing options if an argument that is not an option or an argument to an option is placed between two options. For example, suppose scriptname is a script with valid options x and y, neither of which requires an argument. Then the command causes the script to process the x option but not the y option:

$ scriptname -x filex -y

Arguments to options and the use of colons in the options are described later in the post.

Using the getopts Statement

The getopts statement is most often used as the condition in a while loop, and the case statement is usually the command used to specify the actions to be taken for the various options that can appear on the command line. The variable name used in the case statement is the name of the variable specified in the getopts statement. In the following code fragment, the variable name is opt_char.

while getopts xy opt_char
do
case $opt_char in
    x) print "Option is -x";;
    y) print "Option is -y";;
    +x) print "Option is +x";;
    +y) print "Option is +y";;
esac
done

If -x was given as an option to a script, the value of opt_char is x. If +x was given as an option to a script, the value of opt_char is +x. The correct ways to invoke the script (if you have execute permission on the script) are:

scriptname [+-]x [+-]y

or

scriptname [+-]xy

The while loop is exited when there are no more options to process.

The options on the command line can be given in different ways. The following are some valid possibilities for a script that accepts x and y as options:

scriptname -x -y
scriptname -xy
scriptname +x -y
scriptname +x +y
scriptname +xy

Handling Invalid Options

Unless you program the getopts statement to look for invalid options and to handle an invalid option when it encounters one, the shell gives its own error message. To process invalid options given on the command line, precede the list of single character options with a colon; for example:

while getopts :xy opt_char

The beginning colon causes getopts to:

  • Set the value of the opt_char variable to?
  • Set the value of the OPTARG reserved variable to the name of the invalid option

If the script uses the case statement to match the options from the command line, then the \? pattern is used to match the value of opt_char when an invalid option is encountered. The backslash is necessary to escape the meaning of the ? metacharacter.

$ cat getoptsex.ksh
#!/bin/ksh

# Script name: getoptsex.ksh

USAGE="usage: $0 -x -y"

while getopts :xy opt_char
do
    case $opt_char in
    x)
        echo "You entered the x option"
        ;;
    y)
        echo "You entered the y option"
        ;;
    \?)
        echo "$OPTARG is not a valid option."
        echo "$USAGE"
        ;;
    esac
done
$ ./getoptsex.ksh -y -x -z
You entered the y option
You entered the x option
z is not a valid option.
usage: ./getoptsex.ksh -x -y
$ cat getoptsex.sh
#!/bin/sh

# Script name: getoptsex.sh

USAGE="usage: $0 -x -y"

while getopts :xy opt_char
do
    case $opt_char in
    x)
        echo "You entered the x option"
        ;;
    y)
        echo "You entered the y option"
        ;;
    \?)
        echo "$OPTARG is not a valid option."
        echo "$USAGE"
        ;;
    esac
done
$ ./getoptsex.sh -y -x -z
You entered the y option
You entered the x option
is not a valid option.
usage: ./getoptsex.sh -x -y

Specifying Arguments to Options

If an option requires an argument, then place a colon (:) immediately after the option character in the getopts statement.

while getopts :x:y opt_char

The colon after the x tells the getopts statement that an argument must immediately follow, with or without a space between the x and the argument. After it is executed, the required argument to the x option is assigned to the OPTARG variable.

When the script is executed, the options that require an argument must be followed by their arguments before another option can be given. For example, if x and y are valid options in a script, with x requiring an argument, then the following commands are all valid ways of invoking the script:

scriptname -x x_arg -y
scriptname -xx_arg -y
scriptname -yx x_arg
scriptname -yxx_arg

The argument to an option is placed in the OPTARG variable so that it can be accessed by the statements in the case statement for the corresponding option.

Example of Using the getopts Statement

Consider the following example:

$ cat getopts1.ksh
#!/bin/ksh

# Script name: getopts1.ksh

USAGE="usage: $0 [-d] [-m month]"

year=$(date +%Y)

while getopts :dm: opt_char
do
    case $opt_char in
    d)
        print -n "Date: "       # -d option given
        date
        ;;

    m)
        cal $OPTARG $year       # -m option given with an arg
        ;;

    \?)
        print "$OPTARG is not a valid option."
        print "$USAGE"
        ;;
    esac
done

The USAGE variable holds the value of how the script is to be invoked. The year variable holds the value of the current year (2000). The getopts statement within the while loop specifies that this script has two valid options: d and m. The m option is followed by a colon (:), so it is required to have an argument supplied to it on the command line. The beginning colon in :dm: tells the getopts statement to watch for invalid switches. (It also tells getopts to watch for options on the command line that require an argument but have no argument supplied to them.)

$ ./getopts1.ksh -dk
Date: Wed Nov 25 18:42:35 IST 2009
k is not a valid option.
usage: ./getopts1.ksh [-d] [-m month]

In the preceding example, the -d option is correctly processed. Then the invalid k option is encountered and, therefore, the getopts statement sets the value of opt_char to a ?. Then the \?) case matches the value in opt_char and the corresponding statements are executed.

$ ./getopts1.ksh -d
Date: Wed Nov 25 18:43:11 IST 2009
$ ./getopts1.ksh -m

In the preceding example, you might have expected output because the -m option is valid, but the option requires an argument to perform its action. Remember that the colon after the m in getopts :dm: states that it requires an argument following it on the command line. If there is no argument, the m) case is not matched and processed.

$ ./getopts1.ksh -m 6
   June 2009
 S  M Tu  W Th  F  S
    1  2  3  4  5  6
 7  8  9  10 11 12 13
14 15 16  17 18 19 20
21 22 23 24 25 26 27
28 29 30
$ ./getopts1.ksh -d filex
Date: Wed Nov 25 18:44:49 IST 2009

The last time the getopts1.ksh script is executed, two arguments are given to it: -d and filex. Because filex is not preceded with a - or a + sign, it is not processed as an option to the script.

Forgetting an Argument to an Option

The following getopts2.ksh example script is the getopts1.ksh script with an added pattern (:) in the case statement. This added pattern is matched when an option that requires an argument is given on the command line but is not followed by its argument.

In the example output, the getopts2.ksh script is executed and given the -m option without an argument so that you can see what is printed; otherwise, the script runs the same as the getopts1.ksh script.

$ cat getopts2.ksh
#!/bin/ksh

# Script name: getopts2.ksh

USAGE="usage: $0 [-d] [-m month]"

year=$(date +%Y)

while getopts :dm: opt_char
do
    case $opt_char in
    d)
        print -n "Date: "       # -d option given
        date
        ;;

    m)
        cal $OPTARG $year       # -m option given with an arg
        ;;

    \?)
        print "$OPTARG is not a valid option."
        print "$USAGE"
        ;;

    :)
        print "The $OPTARG option requires an argument."
        print "$USAGE"
        ;;
    esac
done
$ ./getopts2.ksh -m
The m option requires an argument.
usage: ./getopts2.ksh [-d] [-m month]

If you rewrite the script for the Bourne shell, the script looks much the same except the syntax for the following changes:

  • The command substitution for the year variable
  • The print statements are changed to echo
  • If the OPTARG variable is used, it would be null

Therefore, the code resembles:

$ cat getopts2.sh
#!/bin/sh

# Script name: getopts2.ksh

USAGE="usage: $0 [-d] [-m month]"

year=‘date +%Y‘

while getopts :dm: opt_char
do

    case $opt_char in
    d)
        echo "Date: \c"         # -d option given
        date
        ;;

    m)
        cal $OPTARG $year       # -m option given with an arg
        ;;

    \?)
        echo "$OPTARG is not a valid option."
        echo $USAGE
        ;;

    :)
        echo "The $OPTARG option requires an argument."
        echo "$USAGE"
        ;;
    esac
done
$ ./getopts2.sh -m
The  option requires an argument.
usage: ./getopts2sh [-d] [-m month]

In the preceding Bourne shell example, the reference to $OPTARG evaluated to null in the echo statement; otherwise, this script executes the same as the Korn shell version.