0

I am using the customary way of using getopts through a variable named arg. I can capture the option names as follows. Is it possible to detect the moment getopts reaches "--" so that I can issue a message?

while getopts "$shortopts" arg; do
   echo "--> arg: $arg"
   case $arg in
   ("V")
     printf '%s\n' "Version" 
     return
     ;;
   ("u")
     printf '%s\n' "Usage" 
     return
     ;;
   ("h")
     printf '%s\n' "Help" 
     return
     ;;
   esac
done
2
  • Your code contains an unterminated while loop and several unexplained return statements. I'm not sure why you are including this code fragment at all as it doesn't seem to be code that you are actually using.
    – Kusalananda
    Aug 29, 2021 at 17:03
  • 1
    It is not very important, what is important in how to capture the encounter "--" rather than the usual options.
    – Alta
    Aug 29, 2021 at 17:11

3 Answers 3

6

Is it possible to detect the moment getopts reaches "--" so that I can issue a message?

You shouldn't need to.

getopts implements the standard option processing, which means that it stops looking for options when it either sees an argument that's not an option, or if it sees the argument --, which explicitly terminates the list of options. (That first point is different from the GNU custom which looks for options on the whole command line.)

There's no need for the program to care about meeting --.

That said, since getopts doesn't trash the list of positional parameters, you could peek in there to see if the last argument was --.

#!/bin/bash
while getopts a:bc opt; do
    echo "option $opt arg $OPTARG"
done
last=
if [ "$OPTIND" -ge 2 ]; then
        shift "$((OPTIND - 2))"
        last=$1
        shift 1
else
        shift "$((OPTIND - 1))"
fi
if [ "$last" = "--" ]; then
        echo "options were terminated by a double-dash (or last arg was an option-argument '--')"
fi
echo "remaining args: $*"

That would give e.g.

$ bash opts.sh -a blah -- -b foo
option a arg blah
options were terminated by a double-dash (or last arg was an option-argument '--')
remaining args: -b foo

but since it only looks at the last argument, it could be either the -- separator, or -- as an option-argument to some option. E.g. this is the false positive, the -- is not the separator here:

$ bash opts.sh -a -- foo
option a arg --
options were terminated by a double-dash (or last arg was an option-argument '--')
remaining args: foo

Of course you could also implement your own option processing, but it's a bit annoying to do since the shell makes it awkward to process substrings. (You need to recognize -abc as three different options, or as one, or as two, depending on if -a or -b take an option-argument.)

In any case, unless you're doing something far more complex than usually needed, there shouldn't be any reason to look at --. Even if you do something more complex, you might consider doing it outside getopts (and with another separator), similarly to how the expression in find is given, or how GNU parallel takes lists of arguments separated by :::, :::: etc.

7
  • I very much appreciate your analysis. Your comments should suffice in regards to my question.
    – Alta
    Aug 29, 2021 at 17:32
  • Could you explain what is meant by -- as an option-argument? Is there any practical use of -- besides separating options from non-options?
    – Alta
    Aug 29, 2021 at 17:41
  • @Alta, what I mean is that if you have something like getopts a:bc, then the option -a takes an argument as part of the option syntax. If you have -a foo on the command line, getopts returns a in the named variable, and then foo in OPTARG. Now the argument to the option -a, the option-argument, could well be --, because it can be an arbitrary string. You could run grep -e -- file1 file2 to look for the string --. And in general you could have an option like -f <filename>, where someone might want to give the filename -- (it's a valid filename, after all).
    – ilkkachu
    Aug 29, 2021 at 17:50
  • (With grep, there's also the option of running grep -- -- file1 file2, where the first -- is the separator and the second is the first of the remaining command line args, and used as the pattern if -e wasn't used. Looks a bit confusing, right?)
    – ilkkachu
    Aug 29, 2021 at 17:50
  • 1
    I got to understand now. Many thanks.
    – Alta
    Aug 29, 2021 at 18:15
5

After looping getopts it is custom to do:

shift "$((OPTIND - 1))"

the rest of the arguments will then be the ones left. (see for example EXAMPLE¹ section of man getopts 1POSIX).

Point being, from DESCRIPTION:

Any of the following shall identify the end of options: the first "--" argument that is not an option-argument, finding an argument that is not an option-argument and does not begin with a '-', or encountering an error.


Say you have pattern ab:, then (here 4. demonstrating --):

  1. -a x y z
    • -a flag. Rest: x y z
  2. -a -b foo x y z
    • -a flag + -b value = foo. Rest: x y z
  3. -b foo -a -b bar x y z
    • -a flag + -b value = foo and bar. Rest: x y z
    • Both values for -b are passed. Up to the script how to handle.
  4. -a -b foo -- -a -b x y z
    • -a flag + -b value = foo. Rest: -a -b x y z
  5. -a -b foo -x y z
    • Error / unknown option -x
  6. -b foo bar -x y z
    • -b value = foo. Rest: bar -x y z
  7. -b -- -a bar -x y z
    • -a flag + -b value = --. Rest: bar -x y z

As for 7, note that as an argument for an option can have any string including --, or "" for that matter.


¹ though note how that example is missing the quotes around the $((...)); POSIX examples do assume $IFS contains its default value, which is unfortunate as they can't be used in any context.

0
0

Condition your getopts loop on a variable initialized to 0, set it to 1 in the "--") case. E.g.

stop=0
...
while [[ 0 -eq $stop ]] ; do
# getopts call ...
...
"--") stop=1;;r
esac
done
4
  • How would I be able to trigger entry in -- ? Currently I am using myfunc -v 3 -g -- john andrew. Using while getopts "$shortopts" arg; do never gets arg to return --.
    – Alta
    Aug 29, 2021 at 4:15
  • @Alta, you don't, getopts deals with it itself.
    – ilkkachu
    Aug 29, 2021 at 9:25
  • @Alta If that is your issue, it should be part of the question. It's unclear what "handle" in the question means.
    – Kusalananda
    Aug 29, 2021 at 9:52
  • @ilkkachu Basically, using while getopts "$shortopts" arg; do never gets arg to be "--"? There is no other mechanism in getopts that detects "--" ?
    – Alta
    Aug 29, 2021 at 15:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .