‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ On Friday, August 27, 2021 8:52 PM, Robert Elz <k...@munnari.oz.au> wrote:
> Date: Fri, 27 Aug 2021 17:20:39 +0000 > From: nigelberlinguer <nigelberling...@protonmail.com> > > Message-ID: > <MXe0alzC7mwCcivJXNfR4tp7vhikdlq89AZeQHl5yLYKqiEH54YiugyWaTtN2aRkpgcWC6tjkCOCt41IrbPIgx_jMoAkfRzo3NMLvTqV_oE=@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). > > kre Would the code break if I use shortopts="Vuhv:s" (allows getopts to issue errors, not in silent mode) but also have the (":") and ("?") checks inside the case statement? As so 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") vb="$OPTARG" ;; #............................. ("s") sort=1 ;; #............................. (":") printf '%s\n' "Argument not supplied" printf '%s\n' "-${OPTARG} requires an argument." break ;; ("?") printf '%s\n' "Option not recognised by shortopts" printf '%s\n'pfm "Invalid option: -${OPTARG}." break ;; (*) printf '%s\n' "Invoke \`getopts_test -h' for details." ;; esac done shift $(( OPTIND - 1 ))