0

Bash getopts should allow for options and arguments not to depend on position, as stated in tutorials and docs.

The following snippet of code could receive two options:

  • a debug -d option which requires an argument
  • a force -f option, without arguments.

I am seeing the strange behavior that only the first option listed gets processed correctly.

#!/bin/bash

## Defaults 
DEBUG=INFO
forceOption=FALSE

## Usage
printUsage() {
    echo "  "    
    echo "USAGE: $0 [-d <DEBUG_LEVEL>] [ -f ]"
    echo "  "    
    exit 1
} 

#
## Manage options before start
# 

while getopts "hfd:" OPT
do 
    case $OPT in 
        h) printUsage ;;
        f) forceOption="TRUE" ;;
        d) debugLevel="$OPTARG" ;;
        *) printUsage ;;    
    esac
    shift `expr $OPTIND - 1`
done

if [ -n "$debugLevel" ] ; then
    DEBUG="$debugLevel"
fi

echo "DEBUG : $DEBUG"
echo "Force : $forceOption"

You can see the faulty behavior below:

[rgulia$ ~] ./getopts.sh -d WARNING -f 
DEBUG : WARNING
Force : FALSE
[rgulia$ ~] ./getopts.sh -f -d WARNING  
DEBUG : INFO
Force : TRUE

The code parses correctly when the options are used separately or if -f precedes -d in the list of options and they are attached to each other

[rgulia$ ~] ./getopts.sh -f
DEBUG : INFO
Force : TRUE
[rgulia$ ~] ./getopts.sh -d WARNING
DEBUG : WARNING
Force : FALSE
[rgulia$ ~] ./getopts.sh -fd WARNING  
DEBUG : WARNING
Force : TRUE

My impression is that the loop exits too early because it fails to compute OPTIND correctly when an option is passed an argument.

I did some tracing with printf statements for the values of OPT, OPTARG and OPTIND confirms such theory, but I still do not understand why it is happening and how to fix it.

Any suggestions?

rgulia
  • 523
  • 7
  • 11

1 Answers1

1

Take the shift out of the loop. OPTIND is an absolute index into the original arg vector. Do the shift once after the loop

Mischa
  • 2,240
  • 20
  • 18
  • You are right! It works. I think I had misunderstood the meaning of OPTIND all along. So, getopts keeps track of the option and arguments read in the loop and the decrement of OPTIND is only to let that shell know how many arguments are left in the arg vector once -d and -f are processed, right? – rgulia Jul 08 '17 at 16:02
  • yep. Personally I've stopped using getopts style for args. It was designed to be terse (at a time when "copy" was too long, hence "cp") and when most other major OS's (MSDOS, VMS, RSTS) used `command /option=value ..."`# but Unix used "/" in pathnames, so it switched to `-o value`. It's ridiculously easy (in scripts and programs) to scan args[] for strings of the form `option=value` ... and you can make use of parameter defaults set in the environment as well. My 2c – Mischa Jul 08 '17 at 20:44
  • Thank you very much for the explanation! – rgulia Jul 08 '17 at 21:56