4

I'm trying to use getopts in bash to parse command line arguments, but I couldn't figure out how to implement "default" action, if no argument was matched (or no cmdline argument given).

This is silghtly simplified version of what I've tried so far:

while getopts v:t:r:s:c: name;
do
        case $name in
        v) VALIDATE=1;;
        t) TEST=1;;
        r) REPORT=1;;
        s) SYNC=1;;
        c) CLEAR=1;;
        *) print_help; exit 2;;
        \?) print_help; exit 2;;
        esac
done

Is there any (simple) way to make it call print_help; exit 2; on non matching input?

Tomas Pruzina
  • 8,397
  • 6
  • 26
  • 39

4 Answers4

6

Looking between your question and the comments on Aditya's answer, I'd recommend the following:

[getopts]$ cat go
#!/bin/bash

function print_help { echo "Usage" >&2 ; } 

while getopts vtrsc name; do
    case $name in
        v) VALIDATE=1;;
        t) TEST=1;;
        r) REPORT=1;;
        s) SYNC=1;;
        c) CLEAR=1;;
        ?) print_help; exit 2;;
    esac
done

echo "OPTIND: $OPTIND"
echo ${#@}

shift $((OPTIND - 1))

while (( "$#" )); do
    if [[ $1 == -* ]] ; then
        echo "All opts up front, please." >&2 ; print_help ; exit 2
    fi
    echo $1
    shift
done

Since each of those are boolean flag options, you don't need (and in fact, do not want) the arguments, so we get rid of the colons. None of those characters are in IFS, so we don't need to wrap that in quotes, it will be one token for getopts anyway.

Next, we change the \? to a single ? and get rid of the *, as the * would match before the literal \?, and we might as well combine the rules into a single default match. This is a good thing, since any option specified with a - prefix should be an option, and users will expect the program to fail if they specify an option you don't expect.

getopts will parse up to the first thing that isn't an argument, and set OPTIND to that position's value. In this case, we'll shift OPTIND - 1 (since opts are 0-indexed) off the front. We'll then loop through those args by shifting them off, echoing them or failing if they start with a -.

And to test:

[getopts]$ ./go
OPTIND: 1
0
[getopts]$ ./go -t -v go go
OPTIND: 3
4
go
go
[getopts]$ ./go -d -v go go
./go: illegal option -- d
Usage
[getopts]$ ./go -t go -v go -d
OPTIND: 2
5
go
All opts up front, please.
Usage
David Souther
  • 8,125
  • 2
  • 36
  • 53
4

Try the following workaround:

# Parse the arguments.
while getopts ':h?f:' opts; do
  case ${opts} in

    f) # Foo argument.
      ;;

    # My other arguments.

    \? | h | *) # Prints help.
      grep " .)\ #" $0
      exit 0
      ;;
  esac
done

So basically -?/-h would print the parameters with comments based on its own source. Specifying : before options will print help for any other unknown argument also.

kenorb
  • 155,785
  • 88
  • 678
  • 743
  • Nice utilization of grep. Saves time from writing a print help function. I did need to add "h\?" to the arg list to avoid `illegal option` output though. – Wayne Weibel Jan 27 '16 at 16:37
2

v:t:r:s:c: should be in double quotes

 "v:t:r:s:c:"

Based on the script you posted, maybe you don't require all those colons :

Also you don't need *)

Aditya Naidu
  • 1,362
  • 9
  • 12
1

You need to provide a leading colon in the getopts option string if you want to enable ? to match an invalid option -- :vtrsc. Also you don't need the backslash before the ?

glenn jackman
  • 238,783
  • 38
  • 220
  • 352