Bash getopts with Examples

Last updated: March 14, 2023 | Madhur Batra

Command line arguments are a great way to control the behavior of a script without having to rely on another file or environment variables. While shell offers a simple and quick method to handle command line options using positional parameters, this method quickly gets cumbersome for larger and more complex scripts. 

Special shell variables ($0 … $9) store the contents of the command line arguments which can be used in the scripts. This requires the user to pass the parameters in the correct order and the use of each parameter may not be very obvious to the script user.

This is where getopts comes in.

What is Bash getopts?

getopts is a built-in utility in POSIX-compliant shells, such as Bash, and it allows you to define options and arguments for your scripts simply.

It provides an easy interface that reduces the complexity of the script user. It allows you to define options, like -f, and specify whether they require an argument or not. 

Syntax

getopts optstring name [ arg ]
  • optstring contains the recognized option letters. If a letter is followed by a colon, the option is expected to have an argument.
  • name is the variable that receives the options.
  • arg is the optional list of parameters to be processed.
  • getopts uses the built-in variables OPTIND (option index) and OPTARG (option argument) variables to handle the processing and hold the option values. When the script starts, OPTIND is initialized to 1.
  • Each time getopts is invoked and there exists an option, it does the following:
    • places the next option in the variable name.
    • sets the index of the next option to be processed into the built-in OPTIND.
    • It sets the option value into the built-in OPTARG or initializes OPTARG to “” (empty string) if not provided. 

Basic usage

Here is a simple script that defines a single option -h.

#!/bin/bash

function print_usage() {
       echo "simpleObjectColor [OPTIONS VALUE]"
       echo -e "Supported options and values: \n"
       echo "-h                Print usage"

       exit
}

## Define option string
optstring="h"

while getopts ${optstring} arg; do
    case ${arg} in
            h) print_usage;;
            ?) echo -e "Unrecognized option.";;
    esac
done

Now, let’s see how this script works:

  • optstring="h" defines the options string with a single flag -h which doesn’t expect any value.
  • while getopts ${optstring} opt parses the options strings in a loop until no more options are left. 

We execute the script with the -h flag. It prints the usage string and exits.


ubuntu@linuxopsys:~$./simpleObjectColor.sh -h

simpleObjectColor [OPTIONS VALUE]
Supported options and values:

-h                Print usage

Comparison with getopt

Unlike getopts, which is a built-in shell command available in many POSIX-compliant shells such as Bash, getopt is a program that has been created for different Unix and like systems.

There are many functional differences between getopts and getopt:

getoptsgetopt
Only handles short options which are written using a single dash(-) followed by a single character (for ex: -h).Handles both short options and long options, which are written using two dashes (--), followed by a word (for ex: --help).
Limited error handling capability.Relies on the user to ensure that the input is valid.Extensive support for error handling.Ability to report unsupported options or missing option values.
Built-in utility of the POSIX-compliant shells.Available in most shells by default.Not available on all systems by default.Due to large adoption, is now available for installation on all major distributions.

getopts examples

Let's check a few examples to understand the usage of getopts.

Using multiple options

Let’s see how we write a script that can input multiple options and also process values corresponding to each option.

The following script introduces 3 more options: -t, -n, and -c, which also expect values.

#!/bin/bash

function print_usage() {
       echo "simpleObjectColor [OPTIONS VALUE]"
       echo -e "Supported options and values: \n"
       echo "-h               Print usage"
       echo "-t               Type of food (fruit/vegetable/dish)"
       echo "-n               Name of the food (apple/brinjal/pizza)"
       echo "-c               Color of the food"

       exit
}

## Define option string
optstring="ht:n:c:"
i=1

## OPTIND
echo -e "Option index at start: $OPTIND\n"

## Handle different options
while getopts ${optstring} arg; do

    i=$(($i+1))
    case ${arg} in
           h) print_usage;;
            t)  echo -e "Nype: ${OPTARG}";;
            n)  echo -e "Tame: ${OPTARG}";;
            c)  echo -e "Color: ${OPTARG}";;
            ?)  echo -e "Unrecognized option.";;
    esac

    ## OPTIND
    echo -e "Option index after $i getopts invocations: $OPTIND\n"
done

We execute the script with all the 3 supported flags along with values for each.

$./simpleObjectColor.sh -t fruit -n apple -c red
Option index at start: 1

"Type: fruit"
Option index after 2 getopts invocations: 3

"Name: apple"
Option index after 3 getopts invocations: 5

"Color: red"
Option index after 4 getopts invocations: 7

Option index at end: 7

Now, let’s see how this script works: 

  • “t:n:c:" adds the flags -t, -n, and -c which expect arguments due to the ‘:’ after each character.
  • $OPTIND is initialized = 1.
  • On iteration 1, it parses ‘t’ in $arg and “fruit” in $OPTARG.
  • On iteration 2, it parses ‘n’ in $arg and “apple” in $OPTARG.
  • On iteration 3, it parses ‘c’ in $arg and “red” in $OPTARG.
  • At the end $OPTIND = 7 as we pass 6 parameters (3 flags, 3 flag values) to the script.

Using shift command

shift is another built-in command that is frequently used with getopts to shift the command line options and values to the left effectively removing them from the list.

This is really useful as it helps to remove the processed command line options from the argument list.

Let’s see how we can use the shift command in conjunction with getopts.

#!/bin/bash

function print_usage() {
       echo "simpleObjectColor [OPTIONS VALUE]"
       echo -e "Supported options and values: \n"
       echo "-h               Print usage"
       echo "-t               Type of food (fruit/vegetable/dish)"
       echo "-n               Name of the food (apple/brinjal/pizza)"
       echo "-c               Color of the food"

       exit
}

## Define option string
optstring="ht:n:c:"
i=1

## OPTIND
echo -e "Option index at start: $OPTIND\n"

## Handle different options
while getopts ${optstring} arg; do

    i=$(($i+1))
    case ${arg} in
           h) print_usage;;
            t)  echo -e "Nype: ${OPTARG}";;
            n)  echo -e "Tame: ${OPTARG}";;
            c)  echo -e "Color: ${OPTARG}";;
            ?)  echo -e "Unrecognized option.";;
    esac

    ## OPTIND
    echo -e "Option index after $i getopts invocations: $OPTIND\n"
Done

shift  $((OPTIND-1))
echo "The remaining arguments to be processed: $@"

On executing the script with an additional parameter:

$./simpleObjectColor.sh -t fruit -n apple -c red 85
Option index at start: 1

"Type: fruit"
Option index after 2 getopts invocations: 3

"Name: apple"
Option index after 3 getopts invocations: 5

"Color: red"
Option index after 4 getopts invocations: 7

Option index at end: 7
The remaining arguments to be processed: 85

After executing the shift command, and post getopts processing, $@ variable holds the remaining command line arguments.

In this case, the argument list ($@) effectively becomes = (85) which can be further processed out of the getopts loop.

Error handling

When using getopts in a shell script, if it encounters an unexpected option or if it does not find an expected option value, it will print an error message to standard error and exit with a non-zero status code.

Default behavior

Consider the following script:

$ cat errorHandling.sh

#!/bin/bash

## Define option string
optstring="n:"

getopts ${optstring} arg
case ${arg} in
           n) echo -e "Name: ${OPTARG}";;
           ?) echo -e "Unrecognized option, arg: $arg, OPTARG: $OPTARG";;
esac
  • Unexpected option: Passing an invalid flag -m to the script exits the script with the error.
$./errorHandling.sh -m Giraffe
./errorHandling.sh: illegal option -- m
  • Invalid value: Instead if the correct flag -n is passed but no value is specified, it also exits the script with the error.
$./errorHandling.sh -n
./errorHandling.sh: option requires an argument -- n

Silent error handling

If we put a colon (‘:’) at the beginning of the optstring, getopts runs in "silent error checking mode". The script does not terminate or print any verbose error if it encounters an unexpected option or an invalid value.                                                                          Thus, it expects the errors to be handled in the script itself.

Now, consider the following script with a ‘:’ prepended before the existing optstring.

$ cat errorHandling.sh
#!/bin/bash

## Define option string
optstring=":n:"

getopts ${optstring} arg
case ${arg} in
           n) echo -e "Name: ${OPTARG}";;
           ?) echo -e "Unrecognized option, arg: $arg, OPTARG: $OPTARG";;
esac
  • Unexpected option: On passing an invalid flag -m, getopts sets $arg=’?’ and sets $OPTARG=’m’.
$./errorHandling.sh -m
Unrecognized option. arg: ?, OPTARG: m
  • Invalid value: Instead if the correct flag -n is passed but no value is specified, getopts sets $arg=’:’ and sets $OPTARG=’n’.
$./errorHandling.sh -n
Unrecognized option. arg: :, OPTARG: n

Conclusion

In this tutorial, we learned the utility of getopts and how it compares with the getopt.

To conclude, getopts is useful for simple scripts with a small number of arguments and simple values. But for larger and more complex scripts with better error-handling requirements, getopt may be a better choice for CLI parameters handling and processing.

SHARE

Comments

Please add comments below to provide the author your ideas, appreciation and feedback.

Leave a Reply

Leave a Comment