Back in 2007 there was a question posted to the mailing list regarding OPTIND and getopts (http://www.zsh.org/mla/users/2007/msg01188.html). To quote, with some notes and one modifcation: With zsh 4.3.4 the OPTIND incremental bebavior is inconsistent for options with argument and without argument. For option with argument, OPTIND points to next argument, while for option without argument it points to current argument. func() { OPTIND=1 # reset OPTIND each time in to prevent carryover getopts a:b optvar shift $(( OPTIND -1 )) echo "$OPTIND, $1" } func -a x y z output: 3, y # this is the correct output func -b x y z output: 1, -b # this is the questionable output Is this a bug? There is no consistent way to do "shift" as such. Turns out according to the POSIX spec this is a bug ( http://pubs.opengroup.org/onlinepubs/000095399/utilities/getopts.html) When the end of options is encountered, the getopts utility shall exit with a return value greater than zero; the shell variable OPTIND shall be set to the index of the first non-option-argument.... Any of the following shall identify the end of options: the special option "--", finding an argument that does not begin with a '-', or encountering an error. Unless I'm misreading that means that in the first example "y" indicates the end of options so the correct output is "3, y". In the second example the "x" indicates the end of options so the correct output is "2, x". For what it's worth bash and dash set OPTIND in this way. The original email was regarding zsh 4.3.4. I can confirm this is still the behavior at zsh 4.3.9. Simon