Getopts how to handle missing '-' in command line args. | Unix Linux Forums | Shell Programming and Scripting

  Go Back    


Shell Programming and Scripting Post questions about KSH, CSH, SH, BASH, PERL, PHP, SED, AWK and OTHER shell scripts and shell scripting languages here.

Getopts how to handle missing '-' in command line args.

Shell Programming and Scripting


Tags
getopts

Closed Thread    
 
Thread Tools Search this Thread Display Modes
    #1  
Old 09-16-2013
gencon's Avatar
gencon gencon is offline
Registered User
 
Join Date: Mar 2010
Last Activity: 16 December 2013, 11:39 AM EST
Posts: 51
Thanks: 28
Thanked 0 Times in 0 Posts
Getopts how to handle missing '-' in command line args.

I'm using getopts to process command line args in a Bash script. The code looks like this:

Code:
while getopts ":cfmvhs:t:" option; do

    case $option in

        c)   operationMode="CHECK"
             ;;

        f)   operationMode="FAST"
             ;;

        m)   operationMode="MULTI"
             ;;

        v)   verbose="TRUE"
             ;;

        h)   Display_Usage_Message
             exit "$ExitStatusSuccess"
             ;;

        s)   numMulti=$OPTARG
             ;;

        t)   timeoutSeconds=$OPTARG
             ;;

        \?)  Output_Error_Message "OptionInvalid"
             exit "$ExitStatusErrorSwitchInvalid"
             ;;

        :)   Output_Error_Message "ArgMissing" "$OPTARG"
             exit "$ExitStatusErrorArgMissing"
             ;;

    esac
done

My question is how I should spot options which have no '-' prefix? When a user has omitted the '-' on the command line by a mistake. e.g. "progname -f v"

If there is an invalid option such as '-x', 'x' not being in my getopts ":cfmvhs:t:" string then getopts spots that and my Output_Error_Message "OptionInvalid" code is run. BUT if the user omits the '-' by a mistake and just enters 'x' on the command line or the user omits the '-' of one of the valid options then this is not handled. Using "*)" in addition to "\?)" in the case statement does not sort this out.

How should this situation be handled?

Thanks.
Sponsored Links
    #2  
Old 09-16-2013
Corona688 Corona688 is offline Forum Staff  
Mead Rotor
 
Join Date: Aug 2005
Last Activity: 21 August 2014, 1:38 PM EDT
Location: Saskatchewan
Posts: 19,231
Thanks: 773
Thanked 3,223 Times in 3,021 Posts
How is it supposed to tell the difference between a missing -, and a filename named 'x' that the user wants to enter? What if the user really does want to enter a file named x? It can't, its ambiguous.
The Following User Says Thank You to Corona688 For This Useful Post:
gencon (09-16-2013)
Sponsored Links
    #3  
Old 09-16-2013
gencon's Avatar
gencon gencon is offline
Registered User
 
Join Date: Mar 2010
Last Activity: 16 December 2013, 11:39 AM EST
Posts: 51
Thanks: 28
Thanked 0 Times in 0 Posts
That is a very good point. I didn't think about that as the script does not take a filename as input. Oops.

I suppose that after the getopts loop has finished I could loop through the args shifting off valid args and then see if anything is left behind at the end.

Thanks Corona.
    #4  
Old 09-16-2013
Don Cragun's Avatar
Don Cragun Don Cragun is online now Forum Staff  
Moderator
 
Join Date: Jul 2012
Last Activity: 21 August 2014, 7:38 PM EDT
Location: San Jose, CA, USA
Posts: 4,408
Thanks: 172
Thanked 1,487 Times in 1,260 Posts
Hi gencon,
As Corona688 said, the getopts utility is used to parse command line options. When the getopts loop finishes, you should shift off the arguments that it processed and the remaining arguments will be operands for your script. If your script doesn't expect any operands, you can figure that out after the shift. The following rearrangement of your code (with missing defines and functions added and a diagnostic is added at the end if any unexpected operands are found after processing options) may help you understand how to use getopts.

Code:
#!/bin/bash
ExitStatusErrorUnexpectedOperand=1
ExitStatusErrorArgMissing=2
ExitStatusErrorSwitchInvalid=3
ExitStatusSuccess=0
IAm=${0##*/}

function Display_Usage_Message() {
    printf "Usage:\t%s [-c|-f|-m] [-v] [-s numMulti] [-t seconds]\n\t%s -h\n" \
        "$IAm" "$IAm"
}

function Output_Error_Message() {
    printf "%s" "$IAm" >&2 
    printf ": %s" "$@" >&2
    echo >&2
}

while getopts ":cfmvhs:t:" option; do
    case $option in
        (c) operationMode="CHECK"
            ;;
        (f) operationMode="FAST"
            ;;
        (h) Display_Usage_Message
            exit "$ExitStatusSuccess"
            ;;
        (m) operationMode="MULTI"
            ;;
        (s) numMulti=$OPTARG
            ;;
        (t) timeoutSeconds=$OPTARG
            ;;
        (v) verbose="TRUE"
            ;;
        (\?)Output_Error_Message "-$OPTARG" "Unknown option" 
            exit $ExitStatusErrorSwitchInvalid
            ;;
        (:) Output_Error_Message "-$OPTARG" "Option missing option argument"
            exit $ExitStatusErrorArgMissing
            ;;
    esac
done
shift $((OPTIND - 1))
if [ $# -gt 0 ]; then
    msg="$(printf "%d unexpected operand(s) found" $#)"
    Output_Error_Message "$msg" "$@"
    exit $ExitStatusErrorUnexpectedOperand
fi
echo "operationMode is \"$operationMode\""
echo "verbose is \"$verbose\""
printf "numMulti is \"%s\"\n" "$numMulti"
printf "timeoutSeconds is \"%s\"\n" "$timeoutSeconds"
exit $ExitStatusSuccess

Sample invocations of this script with resulting output are included below. Note: Output written to stderr is shown in blue; output written to stdout is shown in black; and input to the shell is shown in green.

Code:
$ myprogram -h
Usage:	myprogram [-c|-f|-m] [-v] [-s numMulti] [-t seconds]
	myprogram -h
$ echo $?
0
$ ./myprogram -fv -s 512 -t30
operationMode is "FAST"
verbose is "TRUE"
numMulti is "512"
timeoutSeconds is "30"
$ echo $?
0
$ ./myprogram -x
myprogram: -x: Unknown option
$ echo $?
3
$ myprogram -m -t
myprogram: -t: Option missing option argument
$ echo $?
2
$ myprogram -v -t 15 operand1 "quoted operand2" operand3
myprogram: 3 unexpected operand(s) found: operand1: quoted operand2: operand3
$ echo $?
1

The Following 3 Users Say Thank You to Don Cragun For This Useful Post:
ahamed101 (09-18-2013), Chubler_XL (09-17-2013), gencon (09-17-2013)
Sponsored Links
    #5  
Old 09-17-2013
gencon's Avatar
gencon gencon is offline
Registered User
 
Join Date: Mar 2010
Last Activity: 16 December 2013, 11:39 AM EST
Posts: 51
Thanks: 28
Thanked 0 Times in 0 Posts
Quote:
Originally Posted by Don Cragun View Post
The following rearrangement of your code ...snip... may help you understand how to use getopts.
Don,

What a thoroughly helpful and informative post, thank you so much. It has helped me to understand what's going on and to resolve my issue with ease. You obviously went to great effort to help me and that was very kind of you and it is greatly appreciated. Cheers Godfather.

Your example allowed me to resolve the situation by adding the following code immediately below the end of the getopts while loop.

Code:
# getopts stops processing args if it encounters an arg without a '-' prefix (unless it is an
# $OPTARG in which case processing continues). Check if any invalid args have been entered by
# working out how many args getopts has actually processed, shifting them off, and if more
# than 0 args remain then an invalid arg must have been entered.

let "numArgsProcessedByGetopts = OPTIND - 1"
shift "$numArgsProcessedByGetopts"
numArgsRemaining="$#"

if [ "$numArgsRemaining" -gt "0" ]; then
    Output_Error_Message "OptionInvalid"
    exit "$ExitStatusErrorSwitchInvalid"
fi

Thanks again.

One quick question... In your case statement you enclosed the patterns inside "()", I have only ever seen (or at least only ever noticed) a final ")". Is there any difference between the 2 styles of notation and, if so, what is the difference?

Code:
# e.g. Do "(c)" and "f)" notation differ in any way?

case $option in
    (c)  operationMode="CHECK"
         ;;
    f)   operationMode="FAST"
         ;;
esac


Last edited by gencon; 09-17-2013 at 12:24 PM..
Sponsored Links
    #6  
Old 09-17-2013
Don Cragun's Avatar
Don Cragun Don Cragun is online now Forum Staff  
Moderator
 
Join Date: Jul 2012
Last Activity: 21 August 2014, 7:38 PM EDT
Location: San Jose, CA, USA
Posts: 4,408
Thanks: 172
Thanked 1,487 Times in 1,260 Posts
Quote:
Originally Posted by gencon View Post
Don,

What a thoroughly helpful and informative post, thank you so much. It has helped me to understand what's going on and to resolve my issue with ease. You obviously went to great effort to help me and that was very kind of you and it is greatly appreciated. Cheers Godfather.

Your example allowed me to resolve the situation by adding the following code immediately below the end of the getopts while loop.

... ... ...
Thanks again.
Hi gencon,
I'm glad it helped.
Quote:
Originally Posted by gencon View Post
One quick question... In your case statement you enclosed the patterns inside "()", I have only ever seen (or at least only ever noticed) a final ")". Is there any difference between the 2 styles of notation and, if so, what is the difference?

Code:
# e.g. Do "(c)" and "f)" notation differ in any way?

case $option in
    (c)  operationMode="CHECK"
         ;;
    f)   operationMode="FAST"
         ;;
esac

It is a personal preference. In the shell, the meaning of (pattern) and pattern) is exactly the same. I prefer the (pattern) form because I use the vi editor (with the showmatch option set) while editing code, and if I try to match the parentheses surrounding a subshell invocation when the subshell contains a case statement, the parentheses will not match up correctly if I use the pattern) form.

Use which ever form looks better to you or works better for you.
Cheers,
Don
The Following User Says Thank You to Don Cragun For This Useful Post:
gencon (09-17-2013)
Sponsored Links
    #7  
Old 09-17-2013
gencon's Avatar
gencon gencon is offline
Registered User
 
Join Date: Mar 2010
Last Activity: 16 December 2013, 11:39 AM EST
Posts: 51
Thanks: 28
Thanked 0 Times in 0 Posts
Quote:
Originally Posted by Don Cragun View Post
It is a personal preference. In the shell, the meaning of (pattern) and pattern) is exactly the same. I prefer the (pattern) form because I use the vi editor (with the showmatch option set) while editing code, and if I try to match the parentheses surrounding a subshell invocation when the subshell contains a case statement, the parentheses will not match up correctly if I use the pattern) form.
Thanks again Don, got it. I think I'll use (pattern) in future cos it looks funkier.

I haven't used vi since my Solaris 2.x days at university but never fully mastered all the keys.

Cheers.
Sponsored Links
Closed Thread

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

More UNIX and Linux Forum Topics You Might Find Helpful
Thread Thread Starter Forum Replies Last Post
How to retrieve command line args one by on. little_wonder Shell Programming and Scripting 1 05-26-2009 12:31 PM
command line args 2 skooly5 UNIX for Dummies Questions & Answers 2 04-06-2008 09:36 PM
command line args skooly5 UNIX for Dummies Questions & Answers 2 04-06-2008 07:46 PM
Command line args enuenu Programming 2 05-29-2007 12:19 AM
required command line args yoi2hot4ya UNIX for Dummies Questions & Answers 6 06-02-2005 06:50 PM



All times are GMT -4. The time now is 07:57 PM.