Bash – Fixing getopts Function Running Only Once

bashfunctiongetopts

I defined the function f in Bash based on the example here (under "An option with an argument"):

f () {
  while getopts ":a:" opt; do
    case $opt in
      a)
        echo "-a was triggered, Parameter: $OPTARG" >&2
        ;;
      \?)
        echo "Invalid option: -$OPTARG" >&2
        return 1
        ;;
      :)
        echo "Option -$OPTARG requires an argument." >&2
        return 1
        ;;
    esac
  done
}

Whereas they use a script, I directly define the function in the shell.

When I first launch Bash and define the function, everything works: f -a 123 prints -a was triggered, Parameter: 123. But when I run the exact same line a second time, nothing is printed.

What's causing this behavior? It happens in Bash 3.2 and 4.3, but it works fine in Zsh 5.1. This is surprising because the example was supposed to be for Bash, not for Zsh.

Best Answer

bash getopts use an environment variable OPTIND to keep track the last option argument processed. The fact that OPTIND was not automatically reset each time you called getopts in the same shell session, only when the shell was invoked. So from second time you called getopts with the same arguments in the same session, OPTIND wasn't changed, getopts thought it had done the job and do nothing.

You can reset OPTIND manually to make it work:

$ OPTIND=1
$ f -a 123
-a was triggered, Parameter: 123

or just put the function into a script and call the script multiple times.


zsh getopts is slightly different. OPTIND was normally reset to 1 each time upon exit from shell function.

Related Question