Skip to content
Argument Parsing with getopts
step 1/7

Reading — step 1 of 7

Learn

~2 min readCommand Substitution and Exit Status

Robust shell scripts accept arguments. getopts is bash's POSIX-compliant tool for parsing short options. The Bash Reference Manual covers it; every production script uses it.

The basics

#!/bin/bash
verbose=0
file=""

while getopts "vf:" opt; do
    case $opt in
        v) verbose=1 ;;
        f) file=$OPTARG ;;
        *) echo "Usage: $0 [-v] [-f file]" >&2; exit 1 ;;
    esac
done
shift $((OPTIND - 1))

echo "verbose=$verbose file=$file remaining=$@"

Usage:

./script.sh -v -f config.txt extra1 extra2
# verbose=1 file=config.txt remaining=extra1 extra2

Key pieces:

  • getopts "vf:" opt — option string. v is a flag (no arg); f: requires an arg.
  • $OPTARG — value of the option that requires an arg
  • $OPTIND — index of the next argument; use shift $((OPTIND - 1)) to consume processed options
  • $@ — remaining positional args after option parsing

Combining short flags

./script.sh -vf config.txt          # -v and -f config.txt

Getopts handles this — -vf foo is the same as -v -f foo.

Required vs optional argument options

while getopts ":hv:f:o:" opt; do        # leading colon = silent mode: getopts sets OPTARG on errors
    case $opt in
        h) show_help; exit 0 ;;
        v) verbose=$OPTARG ;;        # required arg
        f) file=$OPTARG ;;            # required arg
        o) output=$OPTARG ;;          # required arg
        :)  echo "option -$OPTARG needs an argument" >&2; exit 1 ;;
        \?) echo "unknown option: -$OPTARG" >&2; exit 1 ;;
    esac
done

Getopts only handles short options (-v, -f). For LONG options (--verbose, --file), use getopt (different program, GNU extension) or hand-parse.

Hand-parsing for long options

while [[ $# -gt 0 ]]; do
    case "$1" in
        --verbose|-v) verbose=1; shift ;;
        --file|-f) file="$2"; shift 2 ;;
        --output=*) output="${1#*=}"; shift ;;
        --help|-h) show_help; exit 0 ;;
        --) shift; break ;;
        -*) echo "unknown option: $1" >&2; exit 1 ;;
        *) break ;;
    esac
done

More code than getopts but supports --long-options, --option=value, mixed positions.

Reading user input — read

For scripts that PROMPT:

read -r -p "Enter your name: " name
echo "Hello, $name"

read -r -s -p "Password: " password    # silent (no echo)
echo   # newline since silent suppressed it

read -r -t 5 -p "Quick! " answer       # 5-second timeout
if [ -z "$answer" ]; then
    echo "timeout"
fi
  • -r — don't interpret backslashes (almost always desired)
  • -p "prompt" — display prompt
  • -s — silent (passwords)
  • -t SEC — timeout
  • -n N — read exactly N chars

Common mistakes

  • Forgetting shift $((OPTIND - 1)) — leftover arguments include the parsed options.
  • Not handling \? — getopts sets opt to ? for unknown options. Without a case, your script silently misbehaves.
  • Trying to use getopts for long options — doesn't support them. Use GNU getopt or hand-parse.
  • read without -r — interprets backslashes. Almost always wrong; always use -r.
  • No usage / help — every script needs -h or --help showing what flags it accepts.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…