bashcommand-linegetopts

getopts ignores all arguments if an unnamed argument precedes named arguments ("./foo unnamed -n named")


I'm trying to understand why getopts seems to ignore all arguments if an "unnnamed" argument precedes any named arguments.

Using an example from http://wiki.bash-hackers.org/howto/getopts_tutorial,

#!/bin/bash

while getopts ":a" opt; do
  case $opt in
    a)
      echo "-a was triggered!" >&2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      ;;
  esac
done

And observing the outcome:

$ ./opt_test
$ ./opt_test -a
-a was triggered!
$ ./opt_test -a -f
-a was triggered!
Invalid option: -f
$ ./opt_test a -a -f
$ ./opt_test a -a
$ ./opt_test a -f
$ ./opt_test lala -f
$ 

So prepending an unnamed argument (an argument without a dash) seems to make getopts ignore all arguments.

Why is this and how can I work around it? I'd like my program to be able to catch such things and print a usage screen.


Solution

  • Quoting from getopts documentation:

    "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."

    Explanatory Notes:

    1. getopts() supports the Utility Syntax Guidelines opengroup.org, 2023. Which defines command-line syntax as UTILITY_NAME OPTION+ARGUMENTS OPERANDS, they call unnamed arguments OPERANDS
    2. The Open Group exists to simplify integration with other peoples code. Following their Utility Syntax Guidelines will reduce the complexity of your code maintenance. These are relevant to your question:
    1. multiple arguments can be stated for an option,seperate arguments with a comma
    2. options should precede operands (unnamed) on the command line.
    3. the -- indicates no more options all succeeding arguments are operands.