Guide Bash-Scripting Intermediate

Command-Line Arguments

Positional parameters, shift, getopts, and parsing both short (-e) and long (--environment) flags in Bash scripts.

3 min read

Positional Parameters

#!/usr/bin/env bash
# ./deploy.sh myapp production

echo "Script name: $0"
echo "First arg:   $1"    # myapp
echo "Second arg:  $2"     # production
echo "All args:    $@"
echo "Arg count:   $#"

shift — Consuming Arguments One at a Time

while [ "$#" -gt 0 ]; do
    echo "Processing: $1"
    shift    # drops $1, shifts $2 into $1, $3 into $2, etc.
done

Simple Flag Parsing (Manual)

ENVIRONMENT="dev"
VERBOSE=false

while [ "$#" -gt 0 ]; do
    case "$1" in
        -e|--environment)
            ENVIRONMENT="$2"
            shift 2
            ;;
        -v|--verbose)
            VERBOSE=true
            shift
            ;;
        -h|--help)
            echo "Usage: $0 [-e environment] [-v]"
            exit 0
            ;;
        *)
            echo "Unknown option: $1" >&2
            exit 1
            ;;
    esac
done

echo "Environment: $ENVIRONMENT, Verbose: $VERBOSE"
./deploy.sh --environment production --verbose

getopts — The Built-In Option Parser (Short Flags Only)

#!/usr/bin/env bash
ENVIRONMENT="dev"
VERBOSE=false

while getopts "e:vh" opt; do
    case "$opt" in
        e) ENVIRONMENT="$OPTARG" ;;
        v) VERBOSE=true ;;
        h) echo "Usage: $0 -e environment [-v]"; exit 0 ;;
        \?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
    esac
done
shift $((OPTIND - 1))    # remove processed options, leaving remaining positional args

echo "Environment: $ENVIRONMENT"
echo "Remaining args: $@"
./deploy.sh -e production -v extra_arg

The : after a letter (like e:) means that option requires a value, captured in $OPTARG. getopts only supports single-character flags — it has no built-in support for --long-flags.

Handling Missing/Required Arguments

if [ "$#" -lt 2 ]; then
    echo "Usage: $0 <app_name> <environment>" >&2
    exit 1
fi

APP_NAME="$1"
ENVIRONMENT="$2"

Default Values for Optional Arguments

ENVIRONMENT="${1:-dev}"       # use $1 if given, else default to "dev"
REGION="${2:-us-east-1}"

A Complete Example

#!/usr/bin/env bash
set -euo pipefail

usage() {
    echo "Usage: $0 -e <environment> [-r <region>] [-v]"
    exit 1
}

REGION="us-east-1"
VERBOSE=false

while getopts "e:r:vh" opt; do
    case "$opt" in
        e) ENVIRONMENT="$OPTARG" ;;
        r) REGION="$OPTARG" ;;
        v) VERBOSE=true ;;
        h) usage ;;
        \?) usage ;;
    esac
done

: "${ENVIRONMENT:?environment is required, use -e}"    # fail with a clear message if unset

echo "Deploying to $ENVIRONMENT in $REGION (verbose=$VERBOSE)"

Production Considerations

  • getopts is POSIX-portable but only supports short, single-character flags — for long-flag support (--environment), you need manual case-based parsing (as shown above) or a third-party library.
  • Always validate required arguments early and exit with a clear usage message — silent failures deep in a script are much harder to debug than an immediate, obvious error.
  • shift $((OPTIND - 1)) after a getopts loop is easy to forget — without it, $1 still refers to the first parsed option, not the first remaining positional argument.

Quick Interview Answer

$1, $2, $@, and $# give access to positional arguments; shift consumes them one at a time. getopts is the built-in, POSIX-portable way to parse short flags like -e value, storing the value in $OPTARG — but it doesn’t support long --flag syntax, which requires manual case-based parsing on $1/shift/shift 2 instead.”

Common Mistakes

  • Forgetting shift $((OPTIND - 1)) after a getopts loop, leaving $1 pointing at an already-processed option.
  • Assuming getopts supports --long-flags out of the box — it only handles single-character short flags.
  • Not validating that required arguments were actually provided before using them, leading to confusing downstream errors.

Add More Questions to This Guide

Know a question that should be here? Share it and help the community!

Open Google Form