bashgetopts

getopts only with long options and no short options


I've written a script that processes long options using getopts and automatically parses these into a variable of the same name as the argument, providing that it is contained in my argument list:

#
# get options
#
optslist=h,help,lat,lon,temp,rain,pop,cdate,cyyyy,cmm,cdd,ctmin,ctmax,crain,cpop,tmiss,rmiss,nday,nhead,nheadt,nheadr,nheadp,climfile,datafile,

while getopts h-: OPT; do  # allow -h and -- "with arg"
  # support long options: https://stackoverflow.com/a/28466267/519360
  if [ "$OPT" = "-" ]; then   # long option: reformulate OPT and OPTARG
    OPT="${OPTARG%%=*}"       # extract long option name
    OPTARG="${OPTARG#"$OPT"}" # extract long option argument (may be empty)
    OPTARG="${OPTARG#=}"      # if long option argument, remove assigning `=`
  fi
  found=false
  for arg in ${optslist//,/ } ; do
      case "$OPT" in
          h | help )
          usage
          exit
          ;;
      ${arg} )
          declare ${arg}=${OPTARG}
          found=true
          break
          ;;
      esac
  done
  if [ $found = false ] ; then
      echo bad argument $OPT
      usage 
      exit
  fi 
done

This works okay, so that if I call the script with --cyyyy=2000 then the variable cyyyy contains 2000. However the script contains a bit of a fudge in that it only works if I include at least one manually specified short option in the getopts call, (in this case "h").

If instead I remove this to only have long arguments like this:

while getopts -: OPT; do 

I get the error

line 203: getopts: -:: invalid option

I tried to quote it and other methods, but I can't seem to work out a way to get around this error. I can live with the manual -h option, but was curious to know if there is a solution to this.


Solution

  • When you run getopts -: OPT in dash (a common /bin/sh), it works fine. Bash, however, is convinced that getopts is being handed an option in this scenario. I would call that a bug.

    You can work around this bash issue with getopts -- -: OPT or getopts :-: OPT (which additionally instructs getopts to ignore all errors).


    Separately, you could simplify

    optslist=h,help,lat,lon,temp,rain,pop,cdate,cyyyy,cmm,cdd,ctmin,ctmax,crain,cpop,tmiss,rmiss,nday,nhead,nheadt,nheadr,nheadp,climfile,datafile,
    …
    for arg in ${optslist//,/ }; do
    

    into

    optslist="h help lat lon temp rain pop cdate cyyyy cmm cdd ctmin ctmax crain cpop tmiss rmiss nday nhead nheadt nheadr nheadp climfile datafile"
    …
    for arg in $optslist; do