How to Pass Arguments to a Bash Script

By 

Updated on

4 min read

Bash script arguments represented as connected command-line parameters

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 .

SyntaxDescription
bash script.sh arg1 arg2Run a script with two arguments
$0Script name or path
$1, $2First 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
shiftRemove the first positional argument and move the rest down
getoptsParse short named options such as -p 3000 -v

Positional Parameters

When you run a script, Bash assigns each argument to a numbered variable:

~/greet.shsh
#!/usr/bin/env bash

printf 'Hello, %s!\n' "$1"
printf 'Argument count: %s\n' "$#"

Run the script with one argument:

Terminal
bash greet.sh World
output
Hello, World!
Argument count: 1

The special variables are:

VariableValue
$0The 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:

~/list-args.shsh
#!/usr/bin/env bash

for arg in "$@"; do
    printf 'Argument: %s\n' "$arg"
done
Terminal
bash list-args.sh alpha beta "hello world"
output
Argument: alpha
Argument: beta
Argument: hello world

Always 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}:

~/deploy.shsh
#!/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:

~/backup.shsh
#!/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:

~/process.shsh
#!/usr/bin/env bash

printf 'Script: %s\n' "$0"

while [ $# -gt 0 ]; do
    printf 'Processing: %s\n' "$1"
    shift
done

Each 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:

~/server.shsh
#!/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:

Terminal
bash server.sh -p 3000 -v
output
Starting 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

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