3

The definition of $OPTIND in POSIX shell, bash are quite consistent and intuitive - it is the index of the next arg to be read. However, its behavior in zsh is quite puzzling, and I can't find document for it.

Example:

# ./test.sh:
foo() {
    while getopts "1:2:34:" flag; do
        echo flag: $flag
        echo arg: $OPTARG
        echo ind: $OPTIND
    done &&
    echo .
}

foo -1 1 -2 2 -3 3 -4 4

Now execute:

>>> sh ./test.sh
flag: 1
arg: 1
ind: 3
flag: 2
arg: 2
ind: 5
flag: 3
arg:
ind: 6    <<<< EXPECTED - next arg should be $6
.

>>> bash ./test.sh
flag: 1
arg: 1
ind: 3
flag: 2
arg: 2
ind: 5
flag: 3
arg:
ind: 6    <<<< EXPECTED - next arg should be $6
.

>>> zsh ./test.sh 
flag: 1
arg: 1
ind: 3
flag: 2
arg: 2
ind: 5
flag: 3
arg:
ind: 5   <<<<<< NOTICE HERE
.

This is tested on zsh 5.3.1 (amd64-portbld-freebsd11.0)

KFL
  • 17,162
  • 17
  • 65
  • 89

1 Answers1

2

You should not care much about the value of OPTIND.

What's important is the next argument after all the options are processed:

Following structure works the same way for both bash and zsh:

# ./test.sh:
foo() {
    local OPTIND flag
    while getopts "1:2:34:" flag; do
        echo flag: $flag
        echo arg: $OPTARG
        echo ind: $OPTIND
    done 
    shift $(( OPTIND - 1 ))
    echo "Next arg is $1"
}

foo -1 1 -2 2 -3 3 -4 4

Always include local OPTIND in any function that use getopts otherwise all getopts share a single global OPTIND

Philippe
  • 20,025
  • 2
  • 23
  • 32
  • Thanks. I upvoted. While this particular type of usage of OPTIND works, it still doesn't answer my original question of why. Shifting `OPTIND-1` at the end of the loop isn't the only way of using `getopts`, it's just a commonly used pattern. So in the cases when OPTIND used differently, my concern in the original question still holds, unless this behavior is understood more clearly. – KFL Sep 01 '20 at 03:19
  • Can you give an example use case where OPTIND is used differently and which cannot be covered by the pattern I gave? – Philippe Sep 01 '20 at 07:07
  • That solution did not work for me. `getopts` is still unreliable in zsh. I used a while loop instead of `getopts`. – t7e May 28 '22 at 09:40