-1

How to check for passed options using getopts (man page) in a POSIX shell? (Report, count, and discard.)

My idea is this: Many of my scripts take no options (not even -h for help, I am transitioning to help directly. And so, if any number of options (switches) is given, I want to count them, report them, and simply discard them (Don't do any action on them).

Let's include all 0-9, and both lower and upper a-z (A-Z).


Update: Many of my scripts do not take even files as arguments, and that is the case with this script.


Usage help looks like this:

usage ()
{
cat << EOF
MPV video player subtitles enable/disable toggling
--------------------------------------------------
on              Enable  subtitles toggling in MPV.
off             Disable subtitles toggling in MPV.
help            Print this help message.
version         Print the script version.
EOF
}
3
  • It's unclear to me from that usage message how your script is meant to be invoked or how a user is to get that help message. Commented Jun 20, 2023 at 12:50
  • @StéphaneChazelas ./script help; Is it a bad idea? I might have described the whole thing better I suppose... Commented Jun 20, 2023 at 13:19
  • Then your script takes a non-option argument: a sub-command name that can be at least help, so usage should be Usage: cmd [<sub-command> [args]] with a list of subcommands that includes help for instance. I guess your sub-commands could in turn accept options an non-options. Like help -v for a verbose help. Which you could process with getopts as well (after having shifted the subcommand name and OPTIND=1). Commented Jun 20, 2023 at 13:32

1 Answer 1

2

If your script doesn't take any option, but you want to check what options were (incorrectly) passed and handle the errors by yourself, that's where you prefix the getopts option specification with : where the wrong option character is returned in $OPTARG. Quoting the POSIX specification of the getopts utility (emphasis mine):

If an option character not contained in the optstring operand is found where an option character is expected, the shell variable specified by name shall be set to the <question-mark> (?) character. In this case, if the first character in optstring is a <colon> (:), the shell variable OPTARG shall be set to the option character found, but no output shall be written to standard error; otherwise, the shell variable OPTARG shall be unset and a diagnostic message shall be written to standard error. This condition shall be considered to be an error detected in the way arguments were presented to the invoking application, but shall not be an error in getopts processing.

So you could write for instance:

optnum=0; while getopts : opt; do
    printf >&2 '%s\n' "Illegal option -$OPTARG"
    optnum=$(( optnum + 1 ))
done

if [ "$optnum" -gt 0 ]; then
    printf >&2 '%s\n' "Some ($optnum) option switches were passed to the script, but none expected!"
fi
shift "$(( OPTIND - 1 ))"
if [ "$#" -gt 0 ]; then
  echo "Non-option argument${2+s}:"
  printf ' - %s\n' "$@"
fi

Example:

$ ./that-script -asd --output=asd -- rest
Illegal option -a
Illegal option -s
Illegal option -d
Illegal option --
Illegal option -o
Illegal option -u
Illegal option -t
Illegal option -p
Illegal option -u
Illegal option -t
Illegal option -=
Illegal option -a
Illegal option -s
Illegal option -d
Some (14) option switches were passed to the script, but none expected!
Non-option argument:
 - rest

But a script that takes no option (but still handles --) would generally just be:

PROGNAME=$0
usage() {
  printf >&2 '%s\n' "Usage: $PROGNAME [<file> [<file>...]]"
  exit 1
}
getopts '' opt && usage
shift "$(( OPTIND - 1))"

for file do
  printf 'Got "%s"\n' "$file"
done

And still exit with a failure exit status upon the first error which is the most sensible thing to do.

$ ./that-script -weirdly-named-.txt
./that-script: illegal option -- w
Usage: ./that-script [<file> [<file>...]]
$ ./that-script -- -weirdly-named-.txt
Got "-weirdly-named-.txt"

Where we let getopts output the error message about the invalid option itself by omitting the : prefix.

Of course, after option processing, you can validate the rest of the arguments as needed. For instance, if your script doesn't accept non-option argument either, after the shift call:

if [ "$#" -ne 0 ]; then
  echo>&2 'This script does not accept any argument.'
  usage
fi

Effectively, it can only be called as either that-script or that-script --. Though you might as well skip the getopts and reject any argument whether they're options, the option delimiter or non-options arguments.

or for a Usage: cmd [<type> <length> <value> [<type> <length> <value>]...]:

if [ "$(( $# % 3 ))" -ne 0 ]; then
  echo>&2 'The number of arguments must be a multiple of 3'
  usage
fi

And so on.

0

You must log in to answer this question.

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