If the very first character of optstring is a colon, getopts will not produce any diagnostic messages for missing option arguments or invalid options.
This could be useful if you really need to have more control over the diagnostic messages produced by your script or if you simply don't want anything to appear on the standard error stream if the user provides wonky command line options.
In silent reporting mode (with the initial :), if you want to alert the user of an invalid option, you will have to look for ? in the variable passed to getopts. Likewise, for missing option arguments, it's a :. These are the two errors usually handled by getopts itself, but to do your own error reporting to the user, you will need to catch these separately to be able to give the correct diagnostic message.
In non-silent reporting mode, getopts does its own error reporting on standard error and you just have to catch a * for "any error".
Compare these two examples:
#!/bin/bash
while getopts 'a:b:' opt; do
case "$opt" in
a) printf 'Got a: "%s"\n' "$OPTARG" ;;
b) printf 'Got b: "%s"\n' "$OPTARG" ;;
*) echo 'some kind of error' >&2
exit 1
esac
done
The * case catches any kind of command line parsing error.
$ bash script.sh -a
script.sh: option requires an argument -- a
some kind of error
$ bash script.sh -c
script.sh: illegal option -- c
some kind of error
#!/bin/bash
while getopts ':a:b:' opt; do
case "$opt" in
a) printf 'Got a: "%s"\n' "$OPTARG" ;;
b) printf 'Got b: "%s"\n' "$OPTARG" ;;
:) echo 'missing argument!' >&2
exit 1 ;;
\?) echo 'invalid option!' >&2
exit 1
esac
done
The : case above catches missing argument errors, while the ? case catches invalid option errors (note that ? needs to be escaped or quoted to match a literal ? as it otherwise matches any single character).
$ bash script.sh -a
missing argument!
$ bash script.sh -b
missing argument!
$ bash script.sh -c
invalid option!