Have you ever tried writing a Bash script and felt stuck figuring out how to handle command-line options like -f or --file?
Well, good news! Enter getopts—a built-in Bash tool that makes your scripts dynamic, user-friendly, and efficient. And the best part is, it handles the tricky parts of scripting like validating inputs and managing errors, so you don’t have to!
However, getting the hang of it can feel overwhelming when you’re just starting out. Which is why in this guide, we’ll demystify getopts step by step, showing you how to simplify your scripts, avoid common pitfalls, and elevate your Bash scripting skills.
So let’s get into it.
Sidenote: Want to dive deeper into Bash? Check out my complete BASH scripting course:
You'll learn Shell Scripting fundamentals, master the command line, and get the practice and experience you need to go from beginner to being able to get hired as a DevOps Engineer, SysAdmin, or Network Engineer!
I guarantee that this is the most comprehensive and up-to-date online resource to learn Bash Scripting.
With that out of the way, let’s dive into this 5-minute tutorial…
What is Bash getopts and why should you use it?
Handling command-line arguments in Bash can be frustrating, especially if you’re manually parsing options from the $@ variable.
It’s like reinventing the wheel every time. You end up writing repetitive code to figure out which flags were passed, check if they have values, and ensure the input even makes sense. And let’s face it: it’s easy to make mistakes along the way.
The good news? Bash offers tools to handle this for you.
The 3 main approaches to handling command-line arguments
Option #1. Manual parsing
This brute-force method involves writing your own logic to loop through $@, figure out each flag, and validate inputs manually. While it’s flexible, it’s tedious and prone to errors—especially for larger scripts.
Option #2. Getopt
This is an external tool that supports advanced features like long options (--help). However, its behavior varies between systems—what works on Linux might fail on macOS—making portable scripts a challenge.
Option #3. Getopts
Not to be confused with almost identical external tool getopt, getopts is a built-in Bash command that simplifies handling short options like -u or -p.
It processes options one by one, validates inputs, and assigns values to variables automatically. However, because it’s built into Bash, getopts is consistent across systems, making it portable and beginner friendly.
To really see the difference though, let’s compare manual parsing to getopts.
For example
Imagine your script needs to handle a username (-u) and a password (-p). Here’s how you’d do it manually:
for arg in "$@"; do
case $arg in
-u|--username)
username="$2"
shift
;;
-p|--password)
password="$2"
shift
;;
*)
echo "Unknown option: $arg"
;;
esac
doneAs you can see, this works, but it’s clunky. You’re repeating yourself and handling every edge case manually.
Compare that instead to using getopts:
while getopts "u:p:" opt; do
case $opt in
u) username="$OPTARG" ;;
p) password="$OPTARG" ;;
\?) echo "Invalid option: -$OPTARG" ;;
esac
doneLook at how much cleaner that is! With getopts, you’re writing less code and getting built-in validation and error handling for free.
Here’s a quick comparison at how getopts stacks up against the other methods:
Here’s a quick comparison of how getopts stacks up against the other methods:
| Feature | Manual Parsing | getopt |
getopts |
|---|---|---|---|
| Input validation | Custom logic needed | Advanced, customizable | Built-in, simpler |
| Portability | High | Low | High |
| Ease of use | Low | Moderate | High |
| Long options support | No | Yes | No |
| Error handling | Manual | Advanced | Automatic |
TL;DR
If you’re looking for an easy, reliable way to handle command-line options in your Bash scripts, getopts is the tool for the job.
It’s simple, portable, and saves you from the hassle of writing repetitive parsing logic. While it doesn’t support long options, it’s a perfect fit for most scenarios—especially if you’re just getting started with scripting.
Ready to dive in? Let’s break down how getopts works and how you can use it to make your scripts more dynamic and efficient.
How to use getopts in Bash scripts
It might seem a bit daunting at first, but once you understand the basics, getopts is surprisingly intuitive.
Here’s the general syntax for getopts:
getopts "optstring" variableLet’s take a look at what this code is doing.
Optstring
The optstring defines the options your script accepts.
Each letter in the string represents an option, like -a or -b. But what if an option needs additional information? That’s where the colon (:) comes in.
- If an option requires an argument, you add a colon (
:) immediately after it - Options without a colon don’t need an argument as they’re simple flags
For example
u:means the-uoption requires an argument, like-u Alicehmeans the-hoption doesn’t need an argument. It just acts as a standalone flag
Variable
The variable is where the option currently being processed is stored. Inside your script, you’ll typically use a case statement to handle the variable and decide what to do for each option.
For example
If -u Alice is passed, the variable will hold u, and the argument (Alice) will be stored in $OPTARG. This makes it easy to manage multiple options in a clean, organized way.
A note on usage
getoptsis almost always used inside awhileloop. The loop then ensures that every option passed to the script is processed, one at a time, until there are no more options left. This structure keeps your code efficient and easy to maintain.
Example breakdown: Writing a simple script
Now that we’ve covered the basics, let’s put it all together.
Here’s an example script that uses getopts to handle two options:
-ufor specifying a username (requires an argument)-hfor displaying a help message (no argument needed)
And here’s the code:
#!/bin/bash
while getopts ":u:h" opt; do
case $opt in
u)
username="$OPTARG"
echo "Username: $username"
;;
h)
echo "Help: This script accepts -u <username> and -h for help."
;;
\?)
echo "Invalid option: -$OPTARG"
;;
:)
echo "Option -$OPTARG requires an argument."
;;
esac
doneHere’s what’s happening in the script:
The string "u:h" tells getopts what options the script expects:
u:indicates that-urequires an argumenthindicates that-hdoesn’t need an argument
While the while loop processes each option in turn. The case statement inside the loop handles what to do for each option:
u: When the-uoption is passed, the argument that follows it is stored in$OPTARGand assigned to theusernamevariableh: When the-hoption is passed, the script displays the help message\?: This catches invalid options (like-z) and shows an error message:: This catches cases where an option that requires an argument is missing one, like-uwithout a value
Now that we have our code, let’s make sure everything works as expected.
Testing the script
Save the script as example.sh and make it executable with this command:
chmod +x example.shNow, try running it with the following inputs. I’ll explain what’s happening in each case.
Test #1. Passing a username
./example.sh -u AliceThe -u option is passed, and the argument Alice is assigned to the username variable. The script recognizes this input as valid and outputs the username.
Output:
Username: AliceTest #2. Asking for help
./example.sh -hThe -h option is passed, which is a standalone flag. The script detects this and displays the help message. No arguments are needed for this option, so it processes successfully.
Output:
Help: This script accepts -u <username> and -h for help.Test #3. Passing an invalid option
./example.sh -zThe -z option is not defined in the optstring making it invalid. Because of this, the \? case in the case statement catches it and displays an error message.
Output:
Invalid option: -zTest #4. Forgetting an argument
./example.sh -uThe -u option is passed, but it requires an argument (like a username). Since no argument is provided, the : case in the case statement is triggered, and an error message is displayed.
Output:
Option -u requires an argument.Key points to remember
- When options are passed correctly, they’re processed as expected. For example,
-u AliceassignsAliceto theusernamevariable, and-htriggers the help message - Invalid options, like
-z, are gracefully handled by the\?case - Missing arguments, like passing
-uwithout a value, are caught by the:case and flagged with an appropriate error message
Now that we’ve covered the basics, let’s look at how to handle multiple options and arguments in a single script.
How to handle multiple options and arguments
This is where getopts really shows its power, as it can process combinations of options, handle arguments for some options while ignoring them for others, and even deal with optional arguments if needed.
Expanding the optstring
In the optstring, you simply add more letters and colons to define additional options. Each letter represents a flag, and colons indicate whether an option requires an argument.
For example
abc: means:
-ais a standalone flag-bis another standalone flag-crequires an argument (e.g.,-c value)
You can define as many options as needed, and getopts will process them all.
Writing a script for multiple options
Let’s say we want to write a script that:
- Accepts a
-uoption for a username (requires an argument) - Accepts a
-poption for a password (requires an argument) - Includes a
-voption for verbose mode (no argument needed)
Here’s how the script might look:
#!/bin/bash
while getopts "u:p:v" opt; do
case $opt in
u)
username="$OPTARG"
echo "Username: $username"
;;
p)
password="$OPTARG"
echo "Password: $password"
;;
v)
verbose=true
echo "Verbose mode enabled"
;;
\?)
echo "Invalid option: -$OPTARG"
;;
:)
echo "Option -$OPTARG requires an argument."
;;
esac
doneNow that we have that, let’s see how the script handles multiple options.
Testing the script
Save it as example.sh, make it executable, and test with these inputs.
Also, since we’ve already covered tests for invalid options (like -z) and missing arguments (like -u without a value) in earlier examples, we’ll focus on scenarios unique to handling multiple options here.
Test #1. Combining options
./example.sh -u Alice -p secret123 -vAll options are processed in a single run. The -u and -p options set their respective values, and the -v option enables verbose mode.
Output:
Username: Alice
Password: secret123
Verbose mode enabledTest #2. Using only -v
./example.sh -vThe -v option is processed. Since it doesn’t require an argument, the script simply enables verbose mode and outputs a confirmation message.
Output:
Verbose mode enabledKey takeaways for handling multiple options
- The
optstringcan include as many options as needed, and colons (:) allow you to specify which options require arguments - Options can be combined or passed individually;
getoptsprocesses them all in the order they’re provided - We skipped redundant tests for invalid options and missing arguments here since those behave the same way as in earlier examples
- Error handling remains crucial—always include cases for invalid options (
\?) and missing arguments (:) to make your scripts user-friendly
Speaking of error handling, let’s explore how to combine these features to improve your scripts even more.
Error handling with getopts
Error handling is a core strength of getopts, and it’s even better when paired with user-friendly features like a help message (-h).
By handling errors effectively, you ensure that users receive clear guidance when they make mistakes, making your script easier to use.
In this section, we’ll explore how to integrate error handling with informative messages and explain how to gain more control over error behavior by using a leading colon (:) in the optstring.
How to integrate help with error messages
The key to combining helpful feedback with error handling is using a usage function. This function provides clear instructions on how to use your script and is called whenever an error occurs.
Here’s how it works:
- Define a
usagefunction at the top of your script - Call the
usagefunction whenevergetoptsdetects an invalid option (\?) or a missing argument (:) - Exit the script after displaying the error and help message to ensure the user has time to correct their input
For example:
#!/bin/bash
# Define the usage function
usage() {
echo "Usage: $0 -u <username> -p <password> [-h for help]"
}
# Process options with getopts
while getopts "u:p:h" opt; do
case $opt in
u)
username="$OPTARG"
;;
p)
password="$OPTARG"
;;
h)
usage
exit 0
;;
\?)
echo "Error: Invalid option -$OPTARG"
usage
exit 1
;;
:)
echo "Error: Option -$OPTARG requires an argument."
usage
exit 1
;;
esac
doneSo let’s break down what's happening.
The usage function displays clear instructions on how to use the script. This ensures users always know what the script expects, even if they make a mistake.
However, when an invalid option (\?) is detected, the script:
- Prints an error message (
Invalid option -$OPTARG) - The usage function is called to show correct usage
- The script exits with status code
1to indicate an error
When a missing argument (:) is detected, the same process is followed
- The script prints an error message (
Option -$OPTARG requires an argument) - The usage function is called to guide the user
- The script exits with status code
1
When the -h option is passed:
- The script calls the usage function
- It exits with status code
0(indicating success)
Controlling error behavior with a leading colon (:)
By default, getopts automatically prints error messages for missing arguments.
However, if you want to suppress these messages and handle errors manually, you can add a leading colon (:) to the optstring.
How it works:
Without a leading colon ("u:p:h"):
getoptsprints an error message for missing arguments- It sets
$optto?
With a leading colon (":u:p:h"):
getoptsdoes not print an error message- Instead, it sets
$optto:and places the affected option in$OPTARG, allowing the script to handle it manually
For example:
Let’s modify the script to manually handle errors instead of letting getopts print them using silent error mode (:)
#!/bin/bash
# Define the usage function
usage() {
echo "Usage: $0 -u <username> -p <password> [-h for help]"
}
# Process options with silent error mode
while getopts ":u:p:h" opt; do
case $opt in
u)
username="$OPTARG"
;;
p)
password="$OPTARG"
;;
h)
usage
exit 0
;;
:)
echo "Error: Option -$OPTARG requires an argument."
usage
exit 1
;;
\?)
echo "Error: Invalid option -$OPTARG"
usage
exit 1
;;
esac
doneNow, instead of getopts printing an error when a required argument is missing, the script detects the issue itself and provides a custom message.
Testing the script
Let’s test the script to see how errors and help messages are integrated.
Test #1. Requesting help
./example.sh -hOutput:
Usage: ./example.sh -u <username> -p <password> [-h for help]✅ The -h option correctly triggers the usage function.
Test #2. Invalid option with help
./example.sh -zOutput:
Error: Invalid option -z
Usage: ./example.sh -u <username> -p <password> [-h for help]✅ The script detects an invalid option (-z) and prints an error along with the usage instructions.
Test #3. Missing argument with automatic error handling (getopts default mode)
If the script is using this optstring ("u:p:h", without :):
while getopts "u:p:h" opt; doRunning
./example.sh -uOutput (default getopts behavior, no leading colon):
getopts: option requires an argument -- u🚨 Since no leading colon (:) is used, getopts prints this error automatically instead of allowing the script to handle it.
Test #4: Missing argument with manual error handling (: in the optstring)
If the script is modified to use this optstring (":u:p:h", with :):
./example.sh -uRunning
./example.sh -uOutput (with : in the optstring, silent error mode):):
Error: Option -u requires an argument.
Usage: ./example.sh -u <username> -p <password> [-h for help]✅ Since a leading colon (:) is present, getopts does not print its own error message. Instead, the script detects the missing argument manually and prints a custom error message.
Key takeaways
- The
usagefunction ensures users always know how to properly use the script - Calling
usagealongside error messages for invalid options (\?) or missing arguments (:) provides helpful feedback without overwhelming users - Exiting with appropriate status codes (
0for help,1for errors) is a best practice for professional scripts
Common pitfalls and how to avoid them
Even though getopts is a powerful tool, it’s easy to run into issues, especially if you’re just starting out. So let’s go over some common pitfalls and how to avoid them, so your scripts can run smoothly.
Pitfall 1: Forgetting the colon for required arguments
If you forget to add a colon (:) after an option in the optstring, getopts won’t treat it as requiring an argument. This can lead to unexpected behavior, like not detecting missing arguments.
For example
while getopts "u:p" opt; do
case $opt in
u)
echo "Username: $OPTARG"
;;
p)
echo "Password: $OPTARG"
;;
esac
doneSo, if you run:
./example.sh -uInstead of flagging an error, getopts processes -u without expecting an argument, leaving $OPTARG empty.
How to fix it
Always add a colon after options that require arguments. In this case, "u:p:" ensures both -u and -p expect arguments.
while getopts "u:p:" opt; doNote: If an option requires an argument but is missing one, getopts will print an error automatically unless the optstring starts with :.
Using ":u:p:" allows the script to detect missing arguments manually by checking if $opt is set to :. This provides more control over error handling.
Pitfall 2: Misinterpreting the OPTARG variable
Some developers assume $OPTARG will always contain a value, but it’s only set for options that require arguments. If an option doesn’t need an argument, $OPTARG will be empty.
For example
while getopts "v" opt; do
case $opt in
v)
echo "Verbose mode: $OPTARG"
;;
esac
doneBecause of this, running ./example.sh -v outputs:
Verbose mode:How to fix it
Avoid relying on $OPTARG for options that don’t require arguments. Use it only for options defined with a colon in the optstring.
Pitfall 3: Missing error handling
Skipping the \? or : cases in your case statement can leave your script vulnerable to invalid inputs or missing arguments. This results in unexpected behavior or unclear error messages.
For example
while getopts "u:p:" opt; do
case $opt in
u|p)
echo "Option: $opt, Argument: $OPTARG"
;;
esac
doneWhat happens:
- Running
./example.sh -zdoes nothing - Running
./example.sh -usilently ignores the missing argument
How to fix it
Always include the \? and : cases to handle errors gracefully and provide feedback to the user.
Pitfall 4: Ignoring positional arguments
getopts only processes options and their arguments, leaving positional arguments (arguments without a - or --) unhandled. This can lead to confusion if users pass additional inputs.
For example
./example.sh -u Alice file.txtIn this case, the file.txt argument is ignored.
How to fix it
After the getopts loop, process the remaining arguments using the $@ or $* variables, like so:
shift $((OPTIND - 1))
echo "Positional arguments: $@"Pitfall 5: Using getopts for long options
We mentioned this earlier, but getopts only supports short options (like -u), not long options (like --username). Trying to use getopts for long options won’t work.
How to fix it
If you need to support long options, consider using the external getopt tool instead. For scripts that only need short options, stick to getopts.
Key takeaways
- Always double-check your
optstringfor colons to avoid misconfigured arguments - Use
$OPTARGonly for options that require arguments - Include proper error handling (
\?and:) to make your script robust - Don’t forget to handle positional arguments if your script requires them
- Remember,
getoptsis for short options—usegetoptif you need long option support
It’s time to try getopts in your own code!
So as you can see, getopts is an incredibly handy tool for handling command-line options in Bash scripts, offering simplicity, portability, and built-in error handling.
Now that you understand how to use it, the next step is to put it into practice! Experiment with creating scripts that use multiple options, handle errors gracefully, and solve real-world problems like file management or automation.
The more you practice, the more confident you’ll become. Start small, build your skills, and soon parsing options will feel effortless.
P.S.
Don’t forget - if you want to fast-track your Bash knowledge and get as much hands-on practice as possible, then check out my complete BASH scripting course:
Updated for 2025, you'll learn Shell Scripting fundamentals, master the command line, and get the practice and experience you need to go from beginner to being able to get hired as a DevOps Engineer, SysAdmin, or Network Engineer!
Plus, once you join, you'll have the opportunity to ask questions in our private Discord community from me, other students, and working DevOps professionals.
Want more Bash content?
If you enjoyed this post, then check out my other Bash guides and tutorials!







