getopts
| Since: | All Linux | |
|---|---|---|
| macOS(2001 Cheetah) | ||
| Bash 1.0(1989) |
getopts is a shell built-in command for parsing command-line options in shell scripts. It processes short options (single-character flags) such as -v or -f filename in combination with a while loop. Because it is POSIX-compliant, it works in bash, dash, zsh, and other major shells.
Syntax
The basic syntax of getopts. The accepted option characters are listed in optstring, and the parsed result is stored in opt.
while getopts "optstring" opt; do
case "$opt" in
a) ... ;;
b) ... ;;
esac
done
Appending a : (colon) after a character in optstring means that option requires an argument. The argument value is stored in the $OPTARG variable.
while getopts "vf:" opt; do
case "$opt" in
v) ... ;; # no argument
f) echo "$OPTARG" ;; # value after -f goes into $OPTARG
esac
done
Placing a : at the very start of optstring enables silent error mode. The shell will not print its own error messages for invalid options, allowing the script to handle errors itself.
while getopts ":vf:" opt; do
case "$opt" in
\?) echo "Invalid option: -$OPTARG" >&2 ;;
:) echo "-$OPTARG requires an argument" >&2 ;;
esac
done
Variable List
| Variable | Description |
|---|---|
| $OPTARG | The argument value for the most recently parsed option. For -f file.txt, this holds file.txt. |
| $OPTIND | The index (starting at 1) of the next argument to be parsed. After the loop, running shift $((OPTIND - 1)) leaves only the remaining positional arguments. |
| $opt (any name) | The variable that getopts stores the current option character in. The name is up to you. When an invalid option is encountered, ? is stored here. |
Sample Code
First, a minimal example to see how it works. When -v is passed, verbose mode is activated.
cat verbose_check.sh
#!/bin/bash
while getopts "v" opt; do
case "$opt" in
v) echo "Verbose mode ON" ;;
esac
done
bash verbose_check.sh -v
Verbose mode ON
An example that uses -f for a file path and -v for verbose output. With argument-taking options (f:), the value is stored in $OPTARG.
dominator_scan.sh
#!/bin/bash
verbose=0
target_file=""
while getopts "vf:" opt; do
case "$opt" in
v) verbose=1 ;;
f) target_file="$OPTARG" ;;
\?) echo "Usage: $0 [-v] [-f file]" >&2; exit 1 ;;
esac
done
if [ "$verbose" -eq 1 ]; then
echo "[VERBOSE] Scan target: $target_file"
fi
echo "Starting scan: $target_file"
Run the following command:
bash dominator_scan.sh -v -f suspects.txt [VERBOSE] Scan target: suspects.txt Starting scan: suspects.txt
A practical example with three options — -v (verbose), -f (file), and -h (help) — plus shift $((OPTIND - 1)) to isolate the remaining positional arguments.
sibyl_analyzer.sh
#!/bin/bash
verbose=0
report_file=""
usage() {
echo "Usage: $0 [-v] [-f file] [-h] [names...]"
echo " -v Show verbose output"
echo " -f file Report file to analyze"
echo " -h Show this help"
exit 0
}
while getopts ":vf:h" opt; do
case "$opt" in
v) verbose=1 ;;
f) report_file="$OPTARG" ;;
h) usage ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
:) echo "-$OPTARG requires an argument" >&2; exit 1 ;;
esac
done
# Discard options and keep only the remaining positional arguments
shift $((OPTIND - 1))
if [ "$verbose" -eq 1 ]; then
echo "[INFO] Report file: ${report_file:-not specified}"
echo "[INFO] Targets: $*"
fi
for name in "$@"; do
echo "Analyzing crime coefficient of $name via Sibyl System..."
done
Run the following command:
bash sibyl_analyzer.sh -v -f crime_report.txt "Kogami Shinya" "Tsunemori Akane" [INFO] Report file: crime_report.txt [INFO] Targets: Kogami Shinya Tsunemori Akane Analyzing crime coefficient of Kogami Shinya via Sibyl System... Analyzing crime coefficient of Tsunemori Akane via Sibyl System...
Overview
Each call to getopts parses one option and returns true (0). When no more options remain, it returns false (non-zero) and the loop exits. $OPTIND starts at 1 and advances with each option processed. An option that takes an argument (e.g., -f value) advances $OPTIND by two.
After the loop, running shift $((OPTIND - 1)) shifts away all parsed options so that $1, $2, ... contain only the remaining non-option arguments. "$@" then gives you all of them.
Stacked flags such as -vf file.txt are also supported. getopts expands them character by character and processes each one in turn.
Common Mistakes
Common Mistake 1: The meaning of a leading : in optstring
Whether or not you put a : at the start of optstring changes how errors are handled.
| Form | Mode | Invalid option | Missing argument |
|---|---|---|---|
getopts "vf:" opt | Normal mode | Shell prints an error; opt is set to ? | Shell prints an error; opt is set to ? |
getopts ":vf:" opt | Silent mode | opt is set to ?; $OPTARG holds the invalid character | opt is set to :; $OPTARG holds the option character |
In normal mode, passing an unknown option causes the shell to automatically write an error message (including the script name) to stderr. In silent mode the shell stays quiet, and you can handle errors entirely through the \? and : branches of your case statement.
bash sibyl_analyzer.sh -x ./sibyl_analyzer.sh: illegal option -- x
The output above comes from normal mode (no leading :). In silent mode you would instead see whatever message you wrote in the case "\?") echo "Invalid option: -$OPTARG" branch.
Common Mistake 2: Long options (--verbose) cannot be handled by getopts
getopts only parses POSIX-style short options (-v, -f, etc.). It does not support long options in GNU style such as --verbose or --file=path.
bash dominator_scan.sh --verbose ./dominator_scan.sh: illegal option -- -
--verbose is treated as the invalid option - and causes an error. To handle long options, use the external command getopt or write a manual case "$1" in ... esac loop.
case_search.sh
#!/bin/bash
verbose=0
report_file=""
while [ "$#" -gt 0 ]; do
case "$1" in
--verbose) verbose=1; shift ;;
--file) report_file="$2"; shift 2 ;;
--file=*) report_file="${1#--file=}"; shift ;;
--) shift; break ;;
-*) echo "Invalid option: $1" >&2; exit 1 ;;
*) break ;;
esac
done
echo "verbose: $verbose, file: $report_file"
echo "Remaining args: $*"
Run the following command:
bash case_search.sh --verbose --file=case_log.txt "Ginoza Nobuchika" verbose: 1, file: case_log.txt Remaining args: Ginoza Nobuchika
If you find any errors or copyright issues, please contact us.