Differences between revisions 3 and 17 (spanning 14 versions)
Revision 3 as of 2009-12-07 01:30:32
Size: 2843
Editor: ozgw
Comment:
Revision 17 as of 2010-07-29 16:28:21
Size: 1221
Editor: GreyCat
Comment:
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
== How do I process options in a bash script? ==

For example, how do I code my bash script to accept a bunch of options like
== Common utility functions (warn, die) ==
(If you were looking for option processing, see [[BashFAQ/035]].) The following functions are frequently asked for in #bash, so we hope you find them useful.
Line 7: Line 6:
foobar -a --include something ##
# warn: Print a message to stderr.
# Usage: warn "message"
#
warn() {
  printf '%s\n' "$@" >&2
}

###
### The following three "die" functions
### depend on the above "warn" function.
###

##
# die (simple version): Print a message to stderr
# and exit with the exit status of the most recent
# command.
# Usage: some_command || die "message"
#
die () {
  local st="$?"
  warn "$@"
  exit "$st"
}

##
# die (explicit status version): Print a message to
# stderr and exit with the exit status given.
# Usage: if blah; then die "message" status_code; fi
#
die() {
  local st="$2"
  warn "$1"
  exit "$st"
}

##
# die (optional status version): Print a message to
# stderr and exit with either the given status or
# that of the most recent command.
# Usage: some_command || die "message" [status code]
#
die() {
  local st="$?"
  case "$2" in
    *[^0-9]*) :;;
    *) st="$2";;
  esac
  warn "$1"
  exit "$st"
}
Line 9: Line 58:

First up, there are some [[http://www.gnu.org/software/libtool/manual/libc/Argument-Syntax.html|GNU and POSIX standards]] for how to do this.

=== do-it-yourself ===

{{{
while "$1"; do
    case "$1" in
        -a|--all) ALL=yes ;shift ;;
        -i|--include) INCLUDE="$2"; shift; shift ;;
        *) echo "$PROG: Bad option '$1', exiting." >&2; exit 1;;
    esac
done
}}}

This is all well and good but it's crufty and it doesn't honour the standards - for example, how do you handle concatenation of single-letter options, how about if the user writes {{{-isomething}}} without a space or {{{--include=something}}}?

=== getopt(1) ===

{{{
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: -n 'example.bash' -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

eval set -- "$TEMP"

while true ; do
        case "$1" in
                -a|--a-long) echo "Option a" ; shift ;;
        .../etc
}}}

This is better as it obeys the standards and gives the user a fairly predictable user-interface. There is still the disadvantage that options are coded in at least 2, probably 3 places - in the call to getopt(1), in the case statement that processes them and presumably in the help message that you are going to get around to writing one of these days. This is a classic opportunity for errors to creep in as the code is written and maintained - often not discovered till much, much later.

=== process-getopt(1) ===

{{{
PROG=$(basename $0)
VERSION='1.2'
USAGE="A tiny example using process-getopt(1)"

# call process-getopt functions to define some options:
source /usr/bin/process-getopt

SLOT=""
SLOT_func() { [ "${1:-""}" ] && SLOT="yes"; } # callback for SLOT option
add_opt SLOT "boolean option" s "" slot

TOKEN=""
TOKEN_func() { [ "${1:-""}" ] && TOKEN="$2"; } # callback for TOKEN option
add_opt TOKEN "this option takes a value" t n token number

add_std_opts # define the standard options --help etc:

TEMP=$(call_getopt "$@") || exit 1
eval set -- "$TEMP" # just as with getopt(1)

# remove the options from the command line
process_opts "$@" || shift "$?"

echo "SLOT=$SLOT"
echo "TOKEN=$TOKEN"
echo "args=$@"
}}}

Here, the options are defined exactly once in the add_opt() function and its associated callback function. A lot of the dirty work is handled automatically and standards are obeyed as in getopt(1) - because it calls getopt for you. As an added bonus you get a nicely formatted help page and a starter for a man page (using a easter-egg option {{{ --print-man-page }}}

... work in progress

Common utility functions (warn, die)

(If you were looking for option processing, see BashFAQ/035.) The following functions are frequently asked for in #bash, so we hope you find them useful.

##
# warn: Print a message to stderr.
# Usage: warn "message"
#
warn() {
  printf '%s\n' "$@" >&2
}

###
### The following three "die" functions
### depend on the above "warn" function.
###

##
# die (simple version): Print a message to stderr
# and exit with the exit status of the most recent
# command.
# Usage: some_command || die "message"
#
die () {
  local st="$?"
  warn "$@"
  exit "$st"
}

##
# die (explicit status version): Print a message to
# stderr and exit with the exit status given.
# Usage: if blah; then die "message" status_code; fi
#
die() {
  local st="$2"
  warn "$1"
  exit "$st"
}

##
# die (optional status version): Print a message to
# stderr and exit with either the given status or
# that of the most recent command.
# Usage: some_command || die "message" [status code]
#
die() {
  local st="$?"
  case "$2" in
    *[^0-9]*) :;;
    *) st="$2";;
  esac
  warn "$1"
  exit "$st"
}


CategoryShell

BashFAQ/101 (last edited 2020-02-04 18:35:41 by GreyCat)