3200
Comment:
|
1784
|
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 "format" ["arguments"...] # warn() { local fmt="$1" shift printf "script_name: $fmt\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" ["arguments"...] # 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 status_code "message" ["arguments"...]; fi # die() { local st="$1" shift warn "$@" 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 [status code] "message" ["arguments"...] # die() { local st="$?" if [[ "$1" != *[^0-9]* ]]; then st="$1" shift fi warn "$@" exit "$st" } |
Line 10: | Line 62: |
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 === |
|
Line 15: | Line 64: |
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 |
## # warn: Print a message to stderr. # Usage: warn "message" # warn() { printf '%s\n' "$@" >&2 } ## # 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 23: | Line 89: |
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, all information about each option is defined in one place making for much easier authoring and maintainence. 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 (for {{{ -h, --help }}} and a starter for a man page (using an easter-egg option {{{ --print-man-page }}} ). [[http://sourceforge.net/projects/process-getopt/ | process-getopt ]] at sourceforge [[http://bhepple.freeshell.org/oddmuse/wiki.cgi/process-getopt | author's website ]] Full disclosure: bhepple at freeshell dot org wrote this entry and is the author of process-getopt(1). Constructive criticism to that email address is most welcome!! |
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 "format" ["arguments"...] # warn() { local fmt="$1" shift printf "script_name: $fmt\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" ["arguments"...] # 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 status_code "message" ["arguments"...]; fi # die() { local st="$1" shift warn "$@" 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 [status code] "message" ["arguments"...] # die() { local st="$?" if [[ "$1" != *[^0-9]* ]]; then st="$1" shift fi warn "$@" exit "$st" }
## # warn: Print a message to stderr. # Usage: warn "message" # warn() { printf '%s\n' "$@" >&2 } ## # 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" }