[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Handling options with optional arguments with getopts

From: Robert Elz
Subject: Re: Handling options with optional arguments with getopts
Date: Sat, 28 Aug 2021 03:52:08 +0700

    Date:        Fri, 27 Aug 2021 17:20:39 +0000
    From:        nigelberlinguer <nigelberlinguer@protonmail.com>

  | It should be noted though, that the POSIX requirement by "Guideline 7"
  | is not guided by actual portability in the technical sense but by a
  | rule written in the POSIX standard.

Those guidelines serve two purposes - they indicate what should be the
arg format for posix standard utilities, and they specify what must be
handled for those cases where they are specified to apply (as in getopts).

  | Perhaps there should be an update
  | to POSIX on what is actually portable or not

There are constantly updates to POSIX - but I don't see anything likely to
change in this area.   "Works in bash" is not the definition of portable.

In general, what POSIX specifies (with just a few exceptions, which
often don't matter) is what you can rely upon working - as soon as you
start using anything not specified by POSIX, or explicitly said
to be unspecified or undefined, then you cannot really expect the
code (including scripts) to work on other systems, and perhaps not
even on later versions of the system you're using.

  | I have seen the following workaround, where tho options that allows an
  | optional argument is defined with no arguments in shortopts.
  |  local vb=1  sort=0
  |  local OPTIND OPTARG
  |  local shortopts="Vuhvs"
  |  while getopts $shortopts arg; do
  |    case $arg in
  |      ("V") printf '%s\n' "Version" ; return ;;
  |      ("u") printf '%s\n' "usage"   ; return ;;
  |      ("h") printf '%s\n' "help"    ; return ;;
  |      ("v")
  |        # Allows argument to be optional.
  |        # Defines option with no arguments in shortopts.
  |        nextarg=${!OPTIND}
  |        if [[ (-n "$nextarg") && ("$nextarg" != -*) ]] ; then
  |          OPTIND=$((OPTIND + 1))
  |          vb="$nextarg"

Aside from using bash private syntax (which could be mostly avoided there
if one had the desire) that kind of use of OPTIND is certainly not portable.
The only defined write operation on OPTIND is to set it to 1.

Further, even where something like that does work, it provides no mechanism
for the arg to the option to begin with a '-', which might not matter in
some cases, but certainly isn't very general.

  | I also wonder whether the "shift" command is used with `getopts`.

No.   Or not inside the loop.   Once the loop is finished, the code
should usually do

        shift $(( ${OPTIND} - 1 ))

to remove all the args that have been processed by getopts - but that's
not always required (there are other ways to get the remaining args, if
any, if they are needed, but doing the shift means the remaining args are "$@").

Altering the arg list (in any way at all) during getopts processing produces
unspecified results.

  | I see people use the `shift` command when doing their own parsing;

Yes, that's often the easiest way to do it for hand rolled parsing
(it means that what you're currently examining is always $1, and so
there's no need to write messy code to get at a variable positional param,
which is not trivial to do portably).

  | and when others use `getopt`.

getopt is obsolete, and has numerous failure modes.   But yes, shift
is used when using getopt (getopt is generally implemented as an
external command, and so cannot affect the state of the shell, including
any shell variables, getopts is always a shell builtin command).


reply via email to

[Prev in Thread] Current Thread [Next in Thread]