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 <[EMAIL PROTECTED]>." 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 <[EMAIL PROTECTED]>." 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 "[EMAIL PROTECTED]"; 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 [EMAIL PROTECTED] exit 0 ------------------------------------------------------------------------ ------------------------------------------------------------------------ ------------------------------------------------------------------------ Thanks! Chris.