Differences between revisions 7 and 13 (spanning 6 versions)
Revision 7 as of 2009-12-07 01:51:28
Size: 3383
Editor: ozgw
Comment:
Revision 13 as of 2009-12-07 05:38:30
Size: 4193
Editor: ozgw
Comment:
Deletions are marked like this. Additions are marked like this.
Line 10: Line 10:
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. First up, there are some GNU and POSIX [[http://www.gnu.org/software/libtool/manual/libc/Argument-Syntax.html|standards]] for how to do this.
Line 19: Line 19:
        *) echo "$PROG: Bad option '$1', exiting." >&2; exit 1;;         -*) echo "$PROG: Bad option '$1', exiting." >&2; exit 1;;
        *) break;;
Line 22: Line 23:
echo args="$@"
Line 24: Line 26:
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? What if the user writes {{{-isomething}}} without a space or {{{--include=something}}}? What about '--' or inter-mixing options and arguments? It's harder than it looks!! This is all well and good and probably portable everywhere - but it's crufty and it doesn't honour the standards. For example, how do you handle concatenation of single-letter options? What if the user writes {{{-isomething}}} without a space or {{{--include=something}}}? What about '--' or inter-mixing options and arguments? It's all a lot harder than it looks!!
Line 28: Line 30:
This example refers to GNU getopt(1), part of the util-linux-ng package. As for other systems - please add info here!!
Line 29: Line 33:
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: -n 'example.bash' -- "$@"` TEMP=`getopt -o ai: --long all,include: -n 'example.bash' -- "$@"`
Line 36: Line 40:
     case "$1" in
                -a|--a-long) echo "Option a" ; shift ;;
        .../etc
    case "$1" in
        -a|--all) echo "Option a" ; shift ;;
        -i|--include) echo "Option i = $2"; shift; shift ;;
        --) break;;
    esa
c
done

echo args="$@"
Line 41: Line 50:
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. 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. You also need to remember to "double shift" options that accept values (eg {{{--include}}} above). Finally, getopt(1) provides no way to print help for the calling script.
Line 45: Line 54:
This relies on a downloadable script that you can install somewhere - in this case, it's in /usr/bin: This depends on bash-2.04 or later and GNU getopt(1) as above.

process-getopt is
a GPL script that you can download and install somewhere - in this case, it's in /usr/bin:
Line 76: Line 87:
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 }}} ). Here, all information about each option is defined in one place making for much easier authoring and maintenance. A lot of the dirty work is handled automatically and standards are obeyed as in getopt(1) - because it calls getopt for you. Options and any option arguments are popped off the command line so that you end up with a "$@" with just the non-option arguments - no need to remember to shift them off. '--' is handled automatically. 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 }}}).
Line 82: Line 93:
[[http://linuxgazette.net/162/hepple.html | Linux Gazette article on process-getopt ]]

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

foobar -a --include something

First up, there are some 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;;
        *) break;;
    esac
done
echo args="$@"

This is all well and good and probably portable everywhere - but it's crufty and it doesn't honour the standards. For example, how do you handle concatenation of single-letter options? What if the user writes -isomething without a space or --include=something? What about '--' or inter-mixing options and arguments? It's all a lot harder than it looks!!

getopt(1)

This example refers to GNU getopt(1), part of the util-linux-ng package. As for other systems - please add info here!!

TEMP=`getopt -o ai: --long all,include: -n 'example.bash' -- "$@"`

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

eval set -- "$TEMP"

while true ; do
    case "$1" in
        -a|--all) echo "Option a" ; shift ;;
        -i|--include) echo "Option i = $2"; shift; shift ;;
        --) break;;
    esac
done

echo args="$@"

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. You also need to remember to "double shift" options that accept values (eg --include above). Finally, getopt(1) provides no way to print help for the calling script.

process-getopt(1)

This depends on bash-2.04 or later and GNU getopt(1) as above.

process-getopt is a GPL script that you can download and install somewhere - in this case, it's in /usr/bin:

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 maintenance. A lot of the dirty work is handled automatically and standards are obeyed as in getopt(1) - because it calls getopt for you. Options and any option arguments are popped off the command line so that you end up with a "$@" with just the non-option arguments - no need to remember to shift them off. '--' is handled automatically. 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 ).

process-getopt at sourceforge

author's website

Linux Gazette article on process-getopt

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!!


CategoryShell

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