Search code examples
bashshellzshgetopts

How to exit recent getopts options?


My script:

 run() {
     while getopts ":dr" option; do
        case "$option" in
            d) echo "__DEBUG__";;
            r) echo "__RELEASE__";;
        esac
    done
 if [ $option -eq ""]
    then 
      echo "__DEBUG__" ; 
 fi
}

Hi, i'm using zsh. When i call run without any options

$ run

$ __ DEBUG __

in the first time it's ok and show DEBUG

Then i call with option (d/r) and call run with no agrument again it show this error

$ run -r

$ __ RELEASE __

$ run

$ run-9: parse error: condition expected: r

I don't know how to fix this, it seem that the while loop still has effect


Solution

  • You need to reset OPTIND before the getopts call at the start of your function, this will not be done automatically in this case. In bash, it is set to 1 when a shell or shell script starts, but not when a function is called.

    This seems to be what POSIX getopts specifies.

    Whenever the shell is invoked, OPTIND shall be initialized to 1.

    zsh getopts behaves differently (thanks @PesaThe):

    OPTIND has an initial value of 1, and is normally set to 1 upon entry to a shell function and restored upon exit.

    Also, when getopts has finished parsing, option will be set to ?, which will not help you determine whether a (valid) option was provided.

    Try something like this:

    run() {
      OPTIND=1
      found=0
      while getopts ":dr" option; do
        case "$option" in
          d) echo "__DEBUG__"; found=1 ;;
          r) echo "__RELEASE__"; found=1 ;;
        esac
      done
      if [ $found -eq 0 ]
      then
        echo "__DEBUG__" ; 
      fi
    }
    

    Or a bit simpler (but not identical to yours, to illustrate):

    run() {
      OPTIND=1
      mode="__DEFAULT__"
      while getopts ":dr" option; do
        case "$option" in
          d) mode="__DEBUG__" ;;
          r) mode="__RELEASE__" ;;
        esac
      done
      echo "$mode"
    }