rdv
rdv

Reputation: 732

Getopts behaves not as expected when option parameter is missing

I have a small script that takes five positional parameters, the first two of which are optional (-v and -p) and the last three mandatory (one, two, three). The second optional parameter takes an argument. So far I have this:

#!/bin/bash

verbose=
altpath=
while getopts ":vp:" opt; do
    case $opt in
        v)  echo "-v triggered"
            verbose=true
            ;;
        p)  echo "-p triggered, param: $OPTARG" 
            altpath=$OPTARG
            ;;
        \?) echo "invalid option: -$OPTARG."
            exit 1
            ;;
        :)  echo "option -$OPTARG requires an argument."
            exit 1
            ;;    
    esac
done

shift "$((OPTIND-1))" 

Now, if I run the script as it should be run, it behaves as expected:

$ myscript -v -p argname one two three

However, if I forget the argname parameter

$ myscript -v -p one two three

it does not say 'option -p requires an argument', but rather takes one as the argument for -p. Not what I want, obviously.

I understand why this happens, but I can't figure out how to solve it.

Upvotes: 1

Views: 3445

Answers (1)

Philipp Claßen
Philipp Claßen

Reputation: 43970

I would recommend to test whether there are three unprocessed arguments left once getopts has finished its work. If it is not the case, abort and print an error message.

For example, add this at the end of the script:

shift "$((OPTIND-1))" 
if [ ! $# -eq 3 ] ; then
    echo "Expected three mandatory arguments"
    exit 1
fi

Forcing an error if the argument of -p is omitted, is not directly supported. As a workaround, you can test whether the next argument is either missing or starts with a dash. For instance:

    p)  if [ -z "$OPTARG" -o "${OPTARG:0:1}" = "-" ] ; then
            echo "Error: -p requires an argument"
            exit 1
        fi
        echo "-p triggered, param: $OPTARG" 
        altpath=$OPTARG
        ;;

Here is the full script:

#!/bin/bash

verbose=
altpath=
while getopts ":vp:" opt; do
    case $opt in
        v)  echo "-v triggered"
            verbose=true
            ;;
        p)  if [ -z "$OPTARG" -o "${OPTARG:0:1}" = "-" ] ; then
                echo "Error: -p requires an argument"
                exit 1
            fi
            echo "-p triggered, param: $OPTARG" 
            altpath=$OPTARG
            ;;
        \?) echo "invalid option: -$OPTARG."
            exit 1
            ;;
        :)  echo "option -$OPTARG requires an argument."
            exit 1
            ;;    
    esac
done

shift "$((OPTIND-1))" 
if [ ! $# -eq 3 ] ; then
    echo "Expected three mandatory arguments"
    exit 1
fi

Upvotes: 3

Related Questions