Search code examples
bashgetopts

getopts in bash, script was working before and now I'm baffled


So I have a couple of getopts in my bash script. Here's an example of a working one.

FOUND=
SEARCH=
COUNT=0
while getopts "ips:flenkc" OPTION
do
case $OPTION in
        i)
                FOUND=1
                let "COUNT++"
                ;;
        p)
                FOUND=2
                let "COUNT++"
                ;;
        s)
                FOUND=3
                SEARCH=$OPTARG
                let "COUNT++"
                ;;
esac
done

Later on a case statement that checks to see if count=1 (meaning, only one of the following, i, p, and s, are used in the call) Not important except that it determines the main action being done.

Now the getopts thing in question. This was working before, and now it's not. The goal is to make it so that if someone wants to input data, they can do so with the following bash command.

./programname -i -f Mary -l Sue -e smary@email.com -n 555-555-5555

Where, when -i is used, we must have -f, -l, -e, and -n (for first name, last name, e-mail, and number). The code I was using: Warning, code is full of syntax errors. If you're learning bash, I highly recommend you do not use anything you see here in my post.

if [ $FOUND == "1" ]
then
        echo "You have chosen to insert things."
        FIRST=
        LAST=
        EMAIL=
        NUMBER=
        while getopts "if:l:e:n:" OPTION
        do
        case $OPTION in
                f)
                        FIRST=$OPTARG
                        ;;
                l)
                        LAST=$OPTARG
                        ;;
                e)
                        EMAIL=$OPTARG
                        ;;
                n)
                        NUMBER=$OPTARG
                        ;;
        esac
        done

        if [[ -z $FIRST ]] || [[ -z $LAST ]] || [[ -z $EMAIL ]] || [[ -z $NUMBER ]]
            echo "Error!!! Some input is missing!!!"
            usage // display usage
        exit 1
        fi
        echo -e $FIRST"\t"$LAST"\t"$EMAIL"\t"$NUMBER >> contacts
fi

Before this program would work, but now, not even a single thing is making it to input for FIRST, LAST, EMAIL, and NUMBER (in my attempts to change the code to see if it was making it to certain steps).

What am I doing wrong with the getopts? It was working fine before, but now.... it's not working at all!


Solution

  • One thing worth noting up front: if your script has already called getopts once, another getopts call will start AFTER all options and therefore effectively do nothing; reset OPTIND to 1 before each subsequent getopts calls to have them reprocess all options.

    Your code has both syntax errors and is worth cleaning up in general:

    • The if [[ -z ... statement was missing a then.
    • The // after usage would have caused a syntax error - POSIX-like shells use # as the comment char.
    • Since this is bash script, stick with using [[ ... ]] consistently (no need for [ ... ]) and/or use (( ... )) for arithmetic operations.
      • Specifically, avoid [ ... == ... ], because it mixes POSIX syntax - [ ... ] - with Bash-specific syntax - == ( POSIX only supports =).
      • If you do use [ ... ], be sure to double-quote variable references, to be safe.
    • No need for multiple [[ ... ]] expressions to OR them together - do it in a single [[ ... || ... || ... ]].
    • It's best to avoid all-uppercase shell-variable names so as to avoid conflicts with environment variables and special shell variables.
    • Output error messages to stderr, using >&2.
    • Enclose the entire argument to echo -e in double-quotes to protect variable values from possibly unwanted expansions.

    Mere syntax errors can usually be caught using shellcheck.net.

    Putting it all together, we get:

    #!/usr/bin/env bash
    
    # ... code that sets $found
    
    # If you've already processed args. with getopts above,
    # you must reset OPTIND to process them again.
    OPTIND=1
    
    if (( found == 1 )) # found is numeric, use arithmetic expression to compare
    then
            echo "You have chosen to insert things."
            first= last= email= number= # don't use all-uppercase var. names
            while getopts "if:l:e:n:" option
            do
              case $option in
                    f)
                            first=$OPTARG
                            ;;
                    l)
                            last=$OPTARG
                            ;;
                    e)
                            email=$OPTARG
                            ;;
                    n)
                            number=$OPTARG
                            ;;
              esac
            done
    
            if [[ -z $first || -z $last || -z $email || -z $number ]]; then
                echo "Error!!! Some input is missing!!!" >&2
                usage # display usage
                exit 1
            fi
            echo -e "$first\t$last\t$email\t$number" >> contacts
    fi