Beginner’s Guide To Bash getopts (With Code Examples)

Andrei Dumitrescu
Andrei Dumitrescu
hero image

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:

Learn Bash scripting in 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!

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
done

As 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
done

Look 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" variable

Let’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 -u option requires an argument, like -u Alice
  • h means the -h option 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

getopts is almost always used inside a while loop. 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:

  • -u for specifying a username (requires an argument)
  • -h for 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
done

Here’s what’s happening in the script:

The string "u:h" tells getopts what options the script expects:

  • u: indicates that -u requires an argument
  • h indicates that -h doesn’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 -u option is passed, the argument that follows it is stored in $OPTARG and assigned to the username variable
  • h: When the -h option 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 -u without 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.sh

Now, try running it with the following inputs. I’ll explain what’s happening in each case.

Test #1. Passing a username

./example.sh -u Alice

The -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: Alice

Test #2. Asking for help

./example.sh -h

The -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 -z

The -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: -z

Test #4. Forgetting an argument

./example.sh -u

The -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 Alice assigns Alice to the username variable, and -h triggers the help message
  • Invalid options, like -z, are gracefully handled by the \? case
  • Missing arguments, like passing -u without 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:

  • -a is a standalone flag
  • -b is another standalone flag
  • -c requires 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 -u option for a username (requires an argument)
  • Accepts a -p option for a password (requires an argument)
  • Includes a -v option 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
done

Now 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 -v

All 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 enabled

Test #2. Using only -v

./example.sh -v

The -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 enabled

Key takeaways for handling multiple options

  • The optstring can include as many options as needed, and colons (:) allow you to specify which options require arguments
  • Options can be combined or passed individually; getopts processes 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).

In this section, we’ll explore how to combine error handling with helpful feedback to guide users.

How to integrate help with error messages

The key to combining help with error handling is the usage function. This function provides clear instructions on how to use your script and is invoked whenever an error occurs.

Here’s how it works:

  1. Define a usage function at the top of your script
  2. Call the usage function whenever getopts detects an invalid option (\?) or a missing argument (:)
  3. Exit the script after displaying the error and help message to ensure the user has time to correct their input

Here’s an 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
done

So 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)
  • Calls usage to show the help message
  • Exits with a non-zero status (exit 1), signaling an error

When a missing argument (:) is detected, the same process is followed: print the error, call usage, and exit.

Finally, when the -h option is passed, the script directly calls usage and exits with a status of 0 (indicating success).

Testing the script

Let’s test the script to see how errors and help messages are integrated.

Test #1. Requesting help

./example.sh -h

Here, the -h option triggers the usage function, which displays usage instructions.

Output:

Usage: ./example.sh -u <username> -p <password> [-h for help]

Test #2. Invalid option with help

./example.sh -z

The script detects the invalid -z option and displays both the error and the help message.

Output:

Error: Invalid option -z
Usage: ./example.sh -u <username> -p <password> [-h for help]

Test #3. Missing argument with help

./example.sh -u

The -u option is missing its required argument. The script displays an error, followed by the help message.

Output:

Error: Option -u requires an argument.
Usage: ./example.sh -u <username> -p <password> [-h for help]

Key takeaways

  • The usage function ensures users always know how to properly use the script
  • Calling usage alongside error messages for invalid options (\?) or missing arguments (:) provides helpful feedback without overwhelming users
  • Exiting with appropriate status codes (0 for help, 1 for 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
done

So, if you run:

./example.sh -u

Instead 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.

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
done

Because 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
done

What happens:

  • Running ./example.sh -z does nothing
  • Running ./example.sh -u silently 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.txt

In 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 optstring for colons to avoid misconfigured arguments
  • Use $OPTARG only 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, getopts is for short options—use getopt if 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:

Learn Bash scripting in 2025

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.


More from Zero To Mastery

Bash Interview Prep: 30 Essential Questions and Answers preview
Bash Interview Prep: 30 Essential Questions and Answers

Ace your Bash interview! Explore 30 key questions with code examples that cover everything from basics to advanced topics. Get interview-ready now.

Beginners Guide to Bash Regex (With Code Examples) preview
Beginners Guide to Bash Regex (With Code Examples)

New to Bash regex? This guide simplifies regex basics with easy-to-follow examples so you can confidently tackle searches and patterns in no time.

How To Use Bash If Statements (With Code Examples) preview
How To Use Bash If Statements (With Code Examples)

Looking to level up your Bash skills with logic? In this guide, I cover everything you need to know about if statements in Bash - with code examples!