How to Pass Arguments to a Bash Script

Arguments let a Bash script behave differently each time you run it. Instead of hard-coding a file path, username, environment name, or port number inside the script, you pass those values on the command line and read them from inside the script.
This guide explains how to pass arguments to Bash scripts, read positional parameters such as $1 and $2, work with all arguments through "$@", set defaults, validate required input, and parse named flags with getopts.
Quick Reference
For a printable quick reference, see the Bash cheatsheet .
| Syntax | Description |
|---|---|
bash script.sh arg1 arg2 | Run a script with two arguments |
$0 | Script name or path |
$1, $2 | First and second positional arguments |
${10} | Tenth positional argument |
$# | Number of arguments passed |
"$@" | All arguments as separate quoted words |
"${1:-default}" | Use a default value when the first argument is missing |
shift | Remove the first positional argument and move the rest down |
getopts | Parse short named options such as -p 3000 -v |
Positional Parameters
When you run a script, Bash assigns each argument to a numbered variable:
#!/usr/bin/env bash
printf 'Hello, %s!\n' "$1"
printf 'Argument count: %s\n' "$#"Run the script with one argument:
bash greet.sh WorldHello, World!
Argument count: 1The special variables are:
| Variable | Value |
|---|---|
$0 | The script name or path |
$1, $2, … | First, second, … argument |
${10}, ${11}, … | Tenth argument and beyond |
$# | Number of arguments passed |
$@ | All arguments as separate words |
$* | All arguments as a single string |
For more detail about each special parameter, see the Bash positional parameters guide.
Access All Arguments
Use $@ to iterate over every argument regardless of how many were passed:
#!/usr/bin/env bash
for arg in "$@"; do
printf 'Argument: %s\n' "$arg"
donebash list-args.sh alpha beta "hello world"Argument: alpha
Argument: beta
Argument: hello worldAlways quote "$@" so arguments with spaces stay as single values. Unquoted $@ allows word splitting, which turns "hello world" into two separate words.
Set Default Values
When an argument is optional, assign a default with ${N:-default}:
#!/usr/bin/env bash
ENV=${1:-production}
BRANCH=${2:-main}
printf 'Deploying %s to %s\n' "$BRANCH" "$ENV"If the caller passes no arguments, ENV becomes production and BRANCH becomes main. If they pass one argument, ENV uses that value and BRANCH falls back to main.
Validate Required Arguments
Exit early with a usage message when a required argument is missing:
#!/usr/bin/env bash
if [ $# -lt 2 ]; then
echo "Usage: $0 SOURCE DESTINATION" >&2
exit 1
fi
SOURCE=$1
DEST=$2
printf 'Ready to back up %s to %s\n' "$SOURCE" "$DEST"$# -lt 2 checks whether fewer than two arguments were provided. The error message goes to stderr with >&2, and the script exits with code 1 to signal failure. After validation succeeds, you can safely use $SOURCE and $DEST in the real backup command, such as rsync.
Shift Through Arguments
shift removes the first argument and renumbers the rest. It is useful when a script accepts a variable number of items to process:
#!/usr/bin/env bash
printf 'Script: %s\n' "$0"
while [ $# -gt 0 ]; do
printf 'Processing: %s\n' "$1"
shift
doneEach shift call moves $2 into $1, $3 into $2, and so on, and decrements $# by one. The loop continues until no arguments remain.
Named Flags with getopts
For scripts with optional flags, getopts parses named options in the style of standard Unix commands:
#!/usr/bin/env bash
PORT=8080
VERBOSE=0
usage() {
echo "Usage: $0 [-p PORT] [-v]" >&2
}
while getopts ":p:v" opt; do
case $opt in
p) PORT=$OPTARG ;;
v) VERBOSE=1 ;;
\?) usage; exit 1 ;;
:) usage; exit 1 ;;
esac
done
shift $((OPTIND - 1))
printf 'Starting server on port %s (verbose: %s)\n' "$PORT" "$VERBOSE"The option string ":p:v" means -p takes a value, -v is a flag with no value, and the leading colon lets the script handle errors itself. $OPTARG holds the value for options that require one. shift $((OPTIND - 1)) removes all parsed flags so $1, $2, and the rest refer to any remaining positional arguments after the flags.
Run it:
bash server.sh -p 3000 -vStarting server on port 3000 (verbose: 1)For a deeper look at option strings, OPTARG, OPTIND, and error handling, read the Bash getopts
guide.
Conclusion
Bash positional parameters handle the straightforward case of ordered arguments, while getopts adds named flag support for more complex scripts. Start with positional parameters, validate the count early, set defaults for optional arguments, and use getopts when your script grows a meaningful set of options. For more on script execution and permissions, see the guide on running Bash scripts
.
Tags
Linuxize Weekly Newsletter
A quick weekly roundup of new tutorials, news, and tips.
About the authors

Dejan Panovski
Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.
View author page