Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getopts - if a flag is empty getopts use the next flag

Tags:

bash

getopts

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?

like image 663
Gili Lapid Avatar asked Dec 29 '15 09:12

Gili Lapid


People also ask

What does getopts mean in Bash?

Updated: 02/01/2021 by Computer Hope. On Unix-like operating systems, getopts is a builtin command of the Bash shell. It parses command options and arguments, such as those passed to a shell script.

What is the purpose of getopts command?

The getopts command is a Korn/POSIX Shell built-in command that retrieves options and option-arguments from a list of parameters. An option begins with a + (plus sign) or a - (minus sign) followed by a character. An option that does not begin with either a + or a - ends the OptionString.

What does colon mean in getopts?

An option character in this string can be followed by a colon (' : ') to indicate that it takes a required argument. If an option character is followed by two colons (' :: '), its argument is optional; this is a GNU extension. getopt has three ways to deal with options that follow non-options argv elements.

What is Optind in getopts?

According to man getopts , OPTIND is the index of the next argument to be processed (starting index is 1). Hence, In sh foo.sh -abc CCC Blank arg1 is -abc , so after a we are still parsing arg1 when next is b ( a 1 ).


2 Answers

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
like image 79
mauro Avatar answered Oct 27 '22 17:10

mauro


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
        ;;
like image 3
Neil Masson Avatar answered Oct 27 '22 16:10

Neil Masson