5

I'm trying to use getopts for a bash script. This script can have flags and all of those flags are mandatory and need to contain a value. When one of the mandatory flags that supposed to contain a value is empty getopts use the next-in-line flag as his content. How do I prevent this?

This is my example:

#!/bin/bash

while getopts "A:B:" OPTION
do
        case $OPTION in
        A)
        GILIA="$GILIA $OPTARG"
        echo GILIA $GILIA
        ;;
        B)
        GILIB="$GILIB $OPTARG"
        echo GILIB $GILIB
        ;;
        esac
done

When using both flags with value:

./test_opt2 -A aaa -B bbb
GILIA aaa
GILIB bbb

When using "-A" flag empty:

./test_opt2 -A  -B bbb
GILIA -B

I know that this is normal/typical behavior of getopts and I'm sure there is a way to go around this...

Any ideas?

Robin Green
  • 32,079
  • 16
  • 104
  • 187
Gili Lapid
  • 53
  • 2
  • 6

3 Answers3

4

Assuming: (1) all options are required and (2) no argument can start with '-' then I will use something like this:

while getopts ":a:b:" opt ; do
    [[ ${OPTARG} == -* ]] && { echo "Missing argument for -${opt}" ; exit 1 ; }
    case ${opt} in
        a ) gilia="$gilia $OPTARG"
            echo gilia $gilia
            ;;
        b ) gilib="$gilib $OPTARG"
            echo gilib $gilib
            ;;  
        \?) echo "Invalid (-${OPTARG}) option"
            ;;
        : ) echo "Missing argument for -${OPTARG}"
            ;;
    esac
done

You will get:

$ ./t.sh -a aaa -b bbb 
gilia aaa
gilib bbb
$ ./t.sh -a -b bbb 
Missing argument for -a
$ ./t.sh -a aaa -b 
gilia aaa
Missing argument for -b
$ ./t.sh -a aaa -b bbb -c ccc
gilia aaa
gilib bbb
Invalid (-c) option
mauro
  • 5,730
  • 2
  • 26
  • 25
  • good approach, only thing I'd mention is that this doesn't cover option when there are no options or arguments passed: "$ ./t.sh", it returns nothing – mauek unak Oct 31 '17 at 11:34
  • Good solution, thanks! One note here: in case you need a POSIX-compliant script, you can replace the argument value check with the following (as a plus, it outputs the error in getopts format): `if (echo "$OPTARG" | grep -Eq ^\\-.+$); then echo "$0: option requires an argument -- ${OPT}" exit 1 fi` – George Herolyants May 03 '21 at 22:30
3

What you could do is to check the value of $OPTARG. If it starts with -, decrease the value of the index $OPTIND, so that the - parameter is picked up as the OPTION next time round the loop.

In your second example, when processing the empty -A argument, $OPTIND will be set to 3. My code will set the value back to 2 so that -B will be processed next.

    case $OPTION in
    A)
        arg=${OPTARG#-}
        if [ $arg == $OPTARG ]
        then
            GILIA="$GILIA $OPTARG"
            echo GILIA $GILIA
        else
            echo "-A needs argument"
            OPTIND=$OPTIND-1
        fi
        ;;
Neil Masson
  • 2,609
  • 1
  • 15
  • 23
2

If you want to pass a blank argument to -A then:

./test_opt2 -A '' -B bbb

If you want the script to accept -A without an argument then change the script:

while getopts "AB:" OPTION

The script will then expect that -A never takes an argument.

Julian
  • 2,837
  • 17
  • 15
  • It doesn't actually answer the question. The problem is to indicate an error if the value for an option is missing by mistake. It's not about allowing to intentionally omit the value or pass an empty value instead. – George Herolyants Apr 30 '21 at 23:35