Bash error: illegal option
getopts prints "illegal option" for a flag missing from the option string. Declare the flag, or use silent mode to handle unknown flags gracefully.
Error String
bash: illegal option -- x
Tldr
A flag was passed that getopts wasn't told about. Add it to the option string, or use silent mode (leading `:`) to catch unknown flags in your own case branch.
Cause
Usually a typo on the command line, or a copy-pasted invocation from a different version of the script. Without explicit handling, getopts prints the platform-default error and continues.
Repro
while getopts "ab" opt; do :; done
# ./script.sh -x
# bash: illegal option -- xFix
while getopts ":ab" opt; do
case $opt in
a|b) ;;
\?) echo "unknown flag -$OPTARG; try -h for help" >&2; exit 64 ;;
esac
doneExplanation
Silent mode also suppresses the default error message and gives you control over wording and exit code — important for scripts shipped to teams.
Faq
Q
Can getopts handle --long-options?
A
No. Use `getopt(1)` from util-linux, or parse `$1` manually in a `while` loop with a `case` on the full word.
Q
Why are my flags ignored after a filename?
A
`getopts` stops at the first non-option argument, so anything after it is positional.
Deep Dive
Heading
The option string drives everything
Body
In `getopts "ab:c" opt`, each letter is an accepted flag and a following colon means that flag takes an argument. Any flag not in the string produces the illegal-option message and sets `opt` to `?`. A leading colon — `getopts ":ab:c" opt` — switches to silent error reporting, letting you print your own usage text instead of the built-in one. Long options such as `--verbose` are not supported by `getopts` at all; they arrive as the single character `-` followed by the rest.
Heading
Shifting and the argument order
Body
After the parse loop you must run `shift $((OPTIND - 1))` so that `$1` becomes the first non-option argument. Forgetting it makes the script reprocess flags as positional parameters. `getopts` also stops at the first non-option word, so `script file.txt -v` never sees `-v`; document that flags come first, or reorder arguments yourself. When calling the script from another script, `--` cleanly separates flags from filenames that begin with a dash.
Checklist
Confirm the failing flag is listed in the option string.
Add `:` after a letter that takes a value, and a leading `:` for custom error messages.
Run `shift $((OPTIND - 1))` after the loop.
Use a case branch for `?` that prints usage and exits non-zero.