Visit Our UNIX and Linux User Community


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


 
Thread Tools Search this Thread
Top Forums Shell Programming and Scripting Getopts how to handle missing '-' in command line args.
# 1  
Old 09-16-2013
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.
# 2  
Old 09-16-2013
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.
This User Gave Thanks to Corona688 For This Post:
# 3  
Old 09-16-2013
That is a very good point. Smilie 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
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

These 3 Users Gave Thanks to Don Cragun For This Post:
# 5  
Old 09-17-2013
Quote:
Originally Posted by Don Cragun
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. Smilie

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 01:24 PM..
# 6  
Old 09-17-2013
Quote:
Originally Posted by gencon
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. Smilie

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
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
This User Gave Thanks to Don Cragun For This Post:
# 7  
Old 09-17-2013
Quote:
Originally Posted by Don Cragun
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. Smilie

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

Cheers.

Previous Thread | Next Thread
Test Your Knowledge in Computers #731
Difficulty: Medium
Microsoft introduced Visual Basic in 1989.
True or False?

10 More Discussions You Might Find Interesting

1. UNIX for Beginners Questions & Answers

Question about getopts optional argument [args...]

There are many places where I can see the syntax description for optargs, which, usually boils down to this: getopts OPTSTRING VARNAME where: OPTSTRING tells getopts which options to expect and where to expect arguments VARNAME tells getopts which shell-variable to use for option reporting... (2 Replies)
Discussion started by: sharkura
2 Replies

2. Emergency UNIX and Linux Support

Cut | command line args

Hi, Can you please hint me how to achieve the below? Input: $./script.sh start 1 2 Internally inside the script i want to set a single variable with $2 and $3 value? Output: CMD=$1 ARGS=$2 $3 --VInodh (10 Replies)
Discussion started by: vino_hymi
10 Replies

3. Shell Programming and Scripting

command line args in unix

Hi, i have a perl script named test.pl. It is executed as cat *.log|test.pl i need the complete command line args. I tried using basename $0 but im getting test.pl only but not cat *.log... Can anyone help me on this. Thanks in advance (3 Replies)
Discussion started by: niteesh_!7
3 Replies

4. Shell Programming and Scripting

How to send a function all command line args?

I have this code, I thought it would automatically know the args sent to script when called from shell. But it seems to not see any... main script: . args . errors . opt . clean dbfile="" opfile="" # calls function in script below chkarg #check commands (2 Replies)
Discussion started by: gcampton
2 Replies

5. Programming

how to handle SQL exceptions to call script having args via java ?

Hi, i want to call shell script via java + in that shell script i m doing some sql operation by connecting to sqlplus . i want to return 0 if successful exeution of SQL operations else 1 ; is it possible ? #!/bin/sh Name=$1; export ORACLE_HOME $ORACLE_HOME/bin/sqlplus... (3 Replies)
Discussion started by: crackthehit007
3 Replies

6. Shell Programming and Scripting

How to retrieve command line args one by on.

Hi, I have to store all the command line arguments into an array. I have the following code. ********************** #! /bin/sh set -A arr_no_updates i=1 while do arr_no_updates=$($i) echo ${arr_no_updates} i=$(($i+1)) done**************** (1 Reply)
Discussion started by: little_wonder
1 Replies

7. UNIX for Dummies Questions & Answers

command line args 2

I have this while loop and at the end I am trying to get it to tell me the last argument I entered. And with it like this all I get is the sentence with no value for $1. Now I tried moving done after the sentence and it printed the value of $1 after every number. I don't want that I just want... (2 Replies)
Discussion started by: skooly5
2 Replies

8. UNIX for Dummies Questions & Answers

command line args

I am trying to print command line arguments one per second. I have this while do echo "6" shift echo "5" shift echo "4" shift echo "3" shift echo "2" shift echo "1" shift done (2 Replies)
Discussion started by: skooly5
2 Replies

9. Programming

Command line args

My program usage takes the form for example; $ theApp 2 "one or more words" i.e. 3 command line arguments; application name, an integer, some text My code includes the following 4 lines: int anInteger; char words; sscanf(argv, "%d", &anInteger); sscanf(argv, "%s", &message); Based... (2 Replies)
Discussion started by: enuenu
2 Replies

10. UNIX for Dummies Questions & Answers

required command line args

Hello, How do I make a command line argument required using getopts? Thanks. (6 Replies)
Discussion started by: yoi2hot4ya
6 Replies

Featured Tech Videos