Creating a Bash script is powerful, but making it flexible and user-friendly is what separates a good script from a great one. The key is allowing users to pass information to your script right from the command line.
This guide will walk you through the two primary ways to process command-line arguments: the simple method using positional parameters and the more robust, industry-standard method using the getopts built-in command.
1. The Basics: Positional Parameters
The simplest way to read arguments is by using positional parameters. Bash automatically assigns arguments to special variables based on their order.
$1: The first argument$2: The second argument- …and so on.
Let’s create a simple script to see this in action.
greet.sh
#!/bin/bash
# Check if at least two arguments are provided
if [ "$#" -lt 2 ]; then
echo "Usage: $0 <greeting> <name>"
exit 1
fi
GREETING=$1
NAME=$2
echo "$GREETING, $NAME!"
How it works:
#!/bin/bash: The shebang, telling the system to use Bash.if [ "$#" -lt 2 ]: This is a check.$#is a special variable that holds the count of arguments. If the count is less than two, the script prints a usage message and exits.$0: Another special variable that holds the name of the script itself.$1and$2: We assign the first and second arguments to our own variables for clarity.
Make the script executable and run it:
chmod +x greet.sh
./greet.sh "Hello there" "Alice"
# Output: Hello there, Alice!
./greet.sh "Good morning"
# Output: Usage: ./greet.sh <greeting> <name>
Other Useful Special Variables:
$@: Expands to all command-line arguments as separate, individually quoted strings ("$1" "$2" "$3" ...). This is the safest way to use all arguments.$*: Expands to all arguments as a single string ("$1 $2 $3 ..."). Use this with caution, as it can cause issues with arguments containing spaces.
Positional parameters are great for simple scripts where the argument order is fixed and obvious. But what if you want optional flags, like -v for verbose output, in any order? That’s when things get complicated and getopts becomes necessary.
2. The Robust Solution: Using getopts
getopts is a Bash built-in command specifically designed to parse options (flags that start with a -) and their arguments. It’s the standard, reliable way to create complex command-line interfaces.
The basic structure of a getopts loop looks like this:
while getopts ":optstring" opt; do
case $opt in
# ... handle options here ...
esac
done
Let’s break down a complete, practical example. Imagine a script that needs an input file, an optional output file, and an optional verbose flag.
process.sh
#!/bin/bash
# --- Default values ---
VERBOSE=false
OUTPUT_FILE=""
# We'll make the input file mandatory later
# --- Help function ---
usage() {
echo "Usage: $0 -i <input_file> [-o <output_file>] [-v] [-h]"
echo " -i <input_file> Specify the input file (mandatory)."
echo " -o <output_file> Specify the output file (optional)."
echo " -v Enable verbose mode."
echo " -h Display this help message."
exit 1
}
# --- Parse options ---
while getopts ":i:o:vh" opt; do
case ${opt} in
i)
INPUT_FILE=$OPTARG
;;
o)
OUTPUT_FILE=$OPTARG
;;
v)
VERBOSE=true
;;
h)
usage
;;
\?)
echo "Invalid option: -$OPTARG" >&2
usage
;;
:)
echo "Option -$OPTARG requires an argument." >&2
usage
;;
esac
done
# --- Shift away the processed options ---
shift $((OPTIND - 1))
# --- Validation ---
if [ -z "${INPUT_FILE}" ]; then
echo "Error: Input file is mandatory."
usage
fi
# --- Main script logic ---
echo "--- Configuration ---"
echo "Input File: ${INPUT_FILE}"
echo "Output File: ${OUTPUT_FILE:-'stdout'}" # Use 'stdout' if OUTPUT_FILE is empty
echo "Verbose Mode: ${VERBOSE}"
echo "Remaining arguments: $@"
echo "---------------------"
if [ "$VERBOSE" = true ]; then
echo "Processing ${INPUT_FILE} verbosely..."
fi
# Your actual script logic would go here
echo "Processing complete."
Dissecting the getopts Script:
usage()function: Every good script should have a help message. This function prints it and exits.while getopts ":i:o:vh" opt; do: This is the core loop.- The first colon (
:) enables silent error handling. It lets us decide how to handle errors instead ofgetoptsprinting its own messages. i:o:vh: This is the “option string”.i:ando:: The colon after a letter means that option requires an argument (e.g.,-i <file>).vandh: Letters without a colon are simple boolean flags (e.g.,-v).
opt: The variable that holds the current option letter being processed (e.g.,i,o,v).
- The first colon (
case ${opt} in: This block handles each option.i): When the-ioption is found, its argument is automatically stored in the$OPTARGvariable. We assign it toINPUT_FILE.v): When the-vflag is found, we set ourVERBOSEvariable totrue.\?): If an unknown option is used (e.g.,-x),getopts(in silent mode) setsoptto?andOPTARGto the invalid option letter. We print an error and show the usage.:): If an option is missing its required argument (e.g., just-i),getoptssetsoptto:andOPTARGto the option letter. We handle that error, too.
shift $((OPTIND - 1)): This is a crucial step!$OPTINDis the index of the next argument to be processed. After the loop, this command removes all the options and their arguments thatgetoptshas already handled, leaving only the “leftover” positional arguments in$@.- Validation: After parsing, we check if mandatory arguments (like
INPUT_FILE) were actually provided.
Running the getopts Script:
Make it executable (chmod +x process.sh) and try these commands:
# Get help
./process.sh -h
# Basic usage
./process.sh -i data.log
# With all options and a leftover argument
./process.sh -v -i data.log -o processed.log extra_arg
# Invalid option
./process.sh -x
# Output: Invalid option: -x
# Missing argument
./process.sh -i
# Output: Option -i requires an argument.
Conclusion
You now have the tools to handle any command-line input your script might need.
- Positional Parameters (
$1,$2,$#): Perfect for simple scripts with a fixed number of required arguments. They are easy to use but not very flexible. getopts: The definitive tool for creating robust, standard-compliant command-line interfaces with optional flags and arguments. It makes your scripts feel like professional Linux commands.
Final Tip: Always include a -h or --help option in your scripts using getopts. It’s a small effort that makes your tools significantly more user-friendly for yourself and others.