Bash script arguments let you pass information into a script from the command line, so the same script can behave differently each time it runs. You read them inside the script using the positional parameters $1, $2, etc., and $@ or $* to refer to all of them. This article explains how they work, how to handle flags and options, and the common pitfalls that trip up new script writers.
What Are Positional Parameters?
When you run a Bash script, everything you type after the script name is stored in special variables called positional parameters. The first argument is $1, the second is $2, and so on. $0 holds the name of the script itself. These are read-only variables—you can’t assign to $1 directly, but you can use shift to move them left (so $2 becomes $1, and so on).
Here’s a minimal example:
#!/bin/bash echo "Script name: $0" echo "First argument: $1" echo "Second argument: $2" echo "All arguments: $@" echo "Number of arguments: $#"
If you save that as args.sh and run ./args.sh hello world, you’ll see:
Script name: ./args.sh First argument: hello Second argument: world All arguments: hello world Number of arguments: 2
Using $@ and $*: The Difference Matters
Both $@ and $* expand to all arguments, but they behave differently when quoted. "$@" treats each argument as a separate word, preserving spaces and empty arguments. "$*" joins all arguments into a single string, separated by the first character of IFS (usually a space).
This becomes critical when an argument contains spaces. For example, if you pass a filename like My File.txt, "$@" will keep it as one argument, while "$*" would turn it into two. The rule of thumb: always use "$@" unless you have a specific reason to join arguments into one string.
#!/bin/bash
for arg in "$@"; do
echo "Argument: $arg"
done
If you run that with ./loop.sh "one two" three, the loop runs twice: once for one two and once for three. If you replace "$@" with "$*", the loop runs only once with the single string one two three.
Checking Argument Count and Providing Help
Most scripts should verify that the right number of arguments were given. The variable $# holds the count. A common pattern is to check for zero arguments and print a usage message:
#!/bin/bash
if [ $# -lt 2 ]; then
echo "Usage: $0 <input_file> <output_file>"
exit 1
fi
input_file="$1"
output_file="$2"
This prevents the script from continuing with missing values, which would otherwise cause confusing errors later. Note that exit 1 signals an error to the caller, which is useful if the script is used in a pipeline or by another script.
Handling Flags and Options (getopts)
Positional parameters work fine for a fixed number of arguments, but many scripts need flags like -v for verbose or -o output.txt for an option with a value. Bash provides the built-in getopts command for this. It parses arguments that start with a dash and lets you define which ones take a value.
#!/bin/bash
verbose=0
output=""
while getopts "vo:" opt; do
case $opt in
v) verbose=1 ;;
o) output="$OPTARG" ;;
*) echo "Invalid option" ; exit 1 ;;
esac
done
The string "vo:" tells getopts that -v takes no argument, and -o requires one (the colon after o). After the loop, you can use shift $((OPTIND-1)) to remove the parsed options, leaving only the positional arguments in $1, $2, etc. This is the standard way to handle options in Bash; it’s portable and avoids the complexity of manually parsing $@.
Common Pitfalls and Mistakes
New users often run into the same issues:
- Forgetting to quote variables. If you use
$1without quotes and the argument contains spaces or wildcards, Bash will split it or expand it. Always write"$1"unless you explicitly want word splitting. - Using
$*instead of"$@"in loops. As mentioned, this can merge arguments unexpectedly. - Assuming arguments start at
$0.$0is the script name, not the first argument. - Not checking
$#before using$1. If no argument is given,$1is empty, which might cause a command to fail silently. - Using
shiftwithout understanding it.shiftdiscards$1and shifts all others down; it’s useful for processing arguments one by one, but you must be careful not to shift past the end.
A common misconception is that $@ and $* are interchangeable. They are not, and the difference only shows up when quoted. Always test with arguments containing spaces.
Advanced: Using shift to Process Arguments Sequentially
Sometimes you want to loop through all arguments without knowing how many there are. The shift command lets you consume arguments one at a time:
#!/bin/bash
while [ $# -gt 0 ]; do
echo "Processing: $1"
shift
done
This works well for simple cases, but for anything more complex than a flat list, getopts is cleaner. A hybrid approach is common: use getopts for flags, then use a loop with shift for remaining positional arguments.
Frequently Asked Questions
How do I pass an argument that starts with a dash?
If an argument starts with -, Bash might treat it as an option. To pass it as a literal value, use -- before it. For example, ./script.sh -- -weird passes -weird as $1. Inside the script, you can also use -- with getopts to signal the end of options.
What is the difference between $@ and $*?
When unquoted, they are identical: both expand to all arguments. When quoted, "$@" preserves each argument as a separate word, while "$*" joins them into one string separated by spaces (or the first character of IFS). Use "$@" in almost all cases.
Can I have more than 9 arguments?
Yes. $1 through $9 are directly accessible, but for $10 and above you must use braces: ${10}, ${11}, etc. Without braces, $10 is interpreted as $1 followed by a literal 0.
How do I pass an empty string as an argument?
Use two quotes: ./script.sh "". The empty string is a valid argument and will be stored in $1. To check for it, use if [ -z "$1" ].
How do I get the last argument?
There is no direct $last variable. You can use ${@: -1} (note the space before the minus) to get the last argument. For the second-to-last, use ${@: -2:1}.
Alternatives to Positional Parameters
For scripts with many options, some people prefer using environment variables or configuration files instead of command-line arguments. This can make the script easier to read, but it also makes it less flexible and harder to use in one-liners. Another alternative is to use read to prompt the user interactively, but that breaks automation. For most scripts, positional parameters plus getopts for options is the right balance of simplicity and power.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Campus Tech Innovation Hubs: What They Are
- Poco C51 Airtel Prepaid Locked Variant India?
- Jenkins vs GitHub Actions: Which CI/CD Fits Your Team?
- Turn Off Backup Encryption in iTunes?
- Shared VPS Hosting: Is It Better?
- Disk Defrag Pro: What It Does and Whether You Need It