help-gnu-utils
[Top][All Lists]
Advanced

[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.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]