user2511788
user2511788

Reputation: 159

Bash - optional argument required but not passed for use in getopts

How do I flag an error in a bash script which requires an argument for getopt, but the user didn't pass it? e.g. the script below requires an argument for option "t":

#!/bin/bash
while getopts "ht:" OPTION
do
    case $OPTION in
        h)
            echo "Hi"
            ;;
        t)
            echo You entered $OPTARG
            ;;
    esac
done

I want to catch the below error and print something else and exit. Currently, it goes on to evaluate more arguments without exiting.

$ ./x.sh -h -t aa     # (this is fine)
Hi
You entered aa

$ ./x.sh -h -t       # (this is not handled)
Hi
No arg for -t option      # (this error is being printed by bash)

Upvotes: 1

Views: 2046

Answers (2)

Thomas Dickey
Thomas Dickey

Reputation: 54475

When it gets a failure, getopts sets OPTION to a question mark (?). You should add that to your case statement. As noted, that is a shell globbing character, requiring escaping.

getopts is a built-in feature of bash. Besides checking for the explicit ? character, you can also use the bash variable OPTERR as mentioned in Using getopts in bash shell script to get long and short command line options.

The best place to find this information is in bash's documentation. Oddly enough (given the number of extensions in bash), it is in a section titled 4.1 Bourne Shell Builtins, and indeed there is a POSIX getopts for reference. The OPTERR variable, however, is a bash extension.

Upvotes: 2

cdarke
cdarke

Reputation: 44344

Several points here:

# Note the leading ':'
while getopts :ht: OPTION
do
    case $OPTION in
        h)
            echo "Hi"
            ;;
        t)
            echo "You entered $OPTARG"
            if [[ ${OPTARG:0:1} == '-' ]]; then
                echo "Invalid value $OPTARG given to -$OPTION" >&2
                exit 1
            fi
            ;;
        :)  echo "$0: -$OPTARG needs a value" >&2; 
            exit 2 
            ;;
        \?) echo "$0: unknown option -$OPTARG" >&2; 
            exit 3
            ;;
    esac
done

The leading ':' on the option list allows us to do our own error handling. If an unknown option is supplied then OPTION is set to a ?. Note that in the case statement this has to be escaped (prefixed with a \), otherwise it would match any single character.

If a value is not supplied to an option, then OPTION is set to a :. Unfortunately this does not help if someone does:

./x -t -h

since the -h will be taken as the OPTARG to option -t. Hence the extra test.

Notice that all the error messages go to standard-error (>&2). To halt the execution of the script we use exit followed by a number in the range 0-255. The only number with a specific meaning is zero, which means success. The numbers 1-255 can have any meaning that we choose, but all imply failure.

Using your examples:

./x.sh -t -h
You entered -h
Invalid value -h given to -t

./x.sh -h -t aa
Hi
You entered aa

./x.sh -h -t
Hi
./x.sh: -t needs a value

./x.sh -t tea -c
You entered tea
./x.sh: unknown option -c

Upvotes: 4

Related Questions