Parsing options with getopts

Bash · Advanced · 10 min

Command-line tools that behave like real Unix commands are easier to use and to call from other scripts.

getopts optstring var parses short options one at a time in a while loop. Each letter in the optstring is an accepted option; a letter followed by : takes a value, which arrives in $OPTARG. A leading : switches to silent error mode, where you handle problems yourself: an unknown option sets var to ? and a missing value sets it to :, with the offending letter in $OPTARG.

getopts handles bundling (-vf), attached values (-ofile) and stops at the first non-option or at --. After the loop, shift $((OPTIND - 1)) removes the parsed options so "$@" holds only the operands (files, names...).

Good habits for script interfaces:

  • a usage function that prints to stderr and exits with status 2,
  • defaults set before the loop,
  • validate operands after shifting ((( $# > 0 )) || usage),
  • support -h for help.

getopts is a bash builtin and POSIX, so it is portable, but it only understands short options. For --long-options, either parse manually with a while (( $# )); do case $1 in ... loop plus shift, or use the util-linux getopt program (note: no s), which is available on Linux but differs on macOS.

Example

usage() { echo "usage: tool [-v] [-o FILE] NAME..." >&2; return 2; }
tool() {
  local verbose=0 output=- opt OPTIND=1
  while getopts ':vo:h' opt; do
    case $opt in
      v) verbose=1 ;;
      o) output=$OPTARG ;;
      h) usage; return ;;
      :) echo "-$OPTARG needs a value" >&2; usage; return ;;
      \?) echo "unknown option -$OPTARG" >&2; usage; return ;;
    esac
  done
  shift $((OPTIND - 1))        # "$@" is now just the operands
  (( $# > 0 )) || { usage; return; }
  echo "verbose=$verbose output=$output names=$*"
}
tool -v -o out.txt a b    # verbose=1 output=out.txt names=a b
tool -vo out.txt a        # verbose=1 output=out.txt names=a  (bundled)
tool -x a                 # unknown option -x / usage: ...
tool -o                   # -o needs a value / usage: ...

Remember

  • Letters in the optstring are options; a trailing : means it takes a value in $OPTARG
  • shift $((OPTIND - 1)) leaves only the operands in "$@"
  • getopts is short-options only; parse --long options manually

Watch out for

  • Forgetting the shift after the loop, so the options are processed again as file names.

Check yourself

  1. In the optstring 'ab:c', which option takes an argument?
  2. What is the purpose of `shift $((OPTIND - 1))` after a getopts loop?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.gnu.org/software/bash/manual/bash.html#Bourne-Shell-Builtins

Builds on

Unlocks

Nothing builds on this yet.