[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
getopts & long options - [was: getopts - mutually exclusive flags]
From: |
Chris Jones |
Subject: |
getopts & long options - [was: getopts - mutually exclusive flags] |
Date: |
Sun, 02 Nov 2008 14:10:55 -0500 |
User-agent: |
Mutt/1.5.13 (2006-08-11) |
On Sat, Nov 01, 2008 at 02:48:38PM EDT, Bob Proulx wrote:
> Chris Jones wrote:
> > How do I handle mutually exclusive flags with getopts?
>
> Not seeing any other answers I might as well chime in. I usually use
> the 'getopt' way and not the 'getopts' way, because I have always used
> getopt. And the GNU version handles long options (--long-opt). GNU
> standards require long options along with --version and --help and
> that pushes me to getopt.
>
> But with getopts you should find this reference is useful:
>
> http://www.opengroup.org/onlinepubs/009695399/utilities/getopts.html
.. rather more detailed than the bash manual description :-)
> > But I'd like to be able to run it manually and specify via flags the
> > backup I want to run - daily/incremental or monthly/full.
> >
> > $ backup -d # daily backup
> > $ backup -m # monthly ..
>
> Sure.
I decided it made better sense to write a separate script for manual
backups since it would be used in very different circumstances.
Makes everything a lot simpler since I need one flag--and one flag only
and everything that's not "d" or "m" means "echo error bad input" +
"exit 1".
> > So far my best effort still finds the following acceptable:
> >
> > $ backup -dm
> >
> > I'm trying to use getopts to implement an exclusive OR of the two
> > options.
>
> As far as I know with all of the option parsing routines available you
> would need to code this type of validation in yourself. The
> combinatorial explosion of combinations of valid and invalid options
> are not something that option parsing libraries can handle. Therefore
> you need to check for validity after the options have been read.
Sounds like a limitation (?) .. Even worse would be:
$ backup -mm
.. which if unchecked might cause the script to run a full backup twice
:-)
OTOH ..
$ backup -vvv
... "very verbose"
Not sure if the latter conforms with GNU coding standards?
> > Incidentally, I am also curious of the "getopts" way to handle the
> > absence of _any_ flags.
>
> Again, AFAIK you would need to handle this in your code.
>
> Here is a quick example of the way that I normally do these types of
> things in my scripts. I am cutting and pasting and may make a typo
> here though so it would all need to be tested. I probably should turn
> this more into a distributable example. But hopefully it will be
> useful as it is regardless.
[...]
> Hope this helps,
> Bob
You bet!
Actually answers other -- more crucial, questions than the one I
initially asked.
.. i.e. coding tactics, strategy, & style.
Priceless .. since it would have taken me forever to (?) come up with
something clean like the above.
To better memorize the teachings of your sample, I used it as a template
for a rough wrapper/frontend of a self-imposed exercise that tries to
work around the "long options" limitation of the getopts bash builtin.
I have half-tested it and it appears to work as long as you don't try
anything too crazy ... still needs adding some input validity checks!
You can try:
myscript -a aarg -b -T --longx xarg --longy yarg -z bigfatfile
myscript -v
myscript -version
myscript -h | --help
etc..
and even
myscript -bTz
but not
myscript -abc
.. but I guess that's not entirely my fault!
One thing that the script (getopts?) does not handle is the duplication
of a flag such as in:
myscript -bbbb
But the way flags are handled in your script -e.g. makes this a moot point
anyway - it just sets the internal 'true/false' flag to 'true' several
times instead of once.
Oh .. more importantly, I've just realized that it does not handle
quoted arguments correctly!
myscript -a "what a slip" bigfatfile
.. will end up with a file named "a slip bigfatfile"
Oh bugger ..
Anyway, here goes:
#!/bin/bash
#-----------------------------------------------------------------------
# ~/bin/gopt0 : kludge to make getopts accept long options
#
# usage : gopt0 [short options] [long options] [object]
#-----------------------------------------------------------------------
###set -o xtrace
#-----------------------------------------------------------------------
# Command help / usage
#-----------------------------------------------------------------------
function gopt0_usage
{
cat <<'WXYZ'
Usage: gopt0 [GNU long option] [option] [filename]
gopt0 accepts the following options:
-a aarg the "a" argument
-b
-c carg the "c" argument
-T
-x | --longx xarg the "x" argument
-y | --longy yarg
-z | --longz ... etc ... etc ..
-h | --help
-v | --version
[refer to Bob's sample & GNU coding standards to finish this!]
WXYZ
}
#-----------------------------------------------------------------------
# Version of command
#-----------------------------------------------------------------------
function gopt0_version
{
echo "$progname $version"
echo "Copyright (C) 2008 Your Name <you@example.com>."
echo "License GPLv3+: GNU GPL version 3 or later"
echo "<http://gnu.org/licenses/gpl.html>"
echo "This is free software: you are free to change & redistribute it."
echo "There is NO WARRANTY, to the extent permitted by law."
echo ""
echo "Written by Your Name <yours@truly.org>."
echo
}
#-----------------------------------------------------------------------
# Substitute short options for long options ..
# Note: there's gotta be a better way to handle 2-dimension tables!!!
#-----------------------------------------------------------------------
function long2short
{
LONG_LST=("longx" \
"longy" \
"longz" \
"help" \
"version"
)
SHRT_LST=("x" \
"y" \
"z" \
"h" \
"v"
)
FOUND='false'; TIX=0
for LOPT in "${LONG_LST[@]}"; do
if [ ${FL:2} = $LOPT ]; then
FL=-"${SHRT_LST[$TIX]}"
FOUND='true'
break
fi
TIX=$(( $TIX + 1 ))
done
if [ $FOUND = 'false' ]; then
echo 'unknown long option: ' $FL
gopt0_usage
exit 1
fi
}
#-----------------------------------------------------------------------
# Now use the getopts builtin to analyze user input
#-----------------------------------------------------------------------
function parse_input
{
echo
while getopts ":a:bc:x:y:zThv" OPTION; do
case $OPTION in
a ) echo "found option -a with argument = $OPTARG" ;;
b ) echo "found option -b" ;;
c ) echo "found option -c with argument = $OPTARG" ;;
x ) echo "found option -x" ;;
y ) echo "found option -y with argument = $OPTARG" ;;
z ) echo "found option -z" ;;
T ) echo "found option -T" ;;
h ) gopt0_usage
exit 0 ;;
v ) gopt0_version
exit 0 ;;
\? ) gopt0_usage
esac
done
shift $((OPTIND - 1))
echo
echo 'All done!! - the file to process is: ' "$*"
echo
}
#-----------------------------------------------------------------------
# Main
#-----------------------------------------------------------------------
I=0
for FL in "$@"; do
if [ ${FL:0:2} = '--' ]; then
long2short
fi
INPUT[$I]=$FL
I=$(( $I + 1 ))
done
parse_input ${INPUT[@]}
exit 0
------------------------------------------------------------------------
------------------------------------------------------------------------
------------------------------------------------------------------------
Thanks!
Chris.