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.
./script help
; Is it a bad idea? I might have described the whole thing better I suppose...help
, so usage should beUsage: cmd [<sub-command> [args]]
with a list of subcommands that includeshelp
for instance. I guess your sub-commands could in turn accept options an non-options. Likehelp -v
for a verbose help. Which you could process withgetopts
as well (after havingshift
ed the subcommand name andOPTIND=1
).