Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trapping getopt invalid options

I'm using getopt (not getops) to provide the ability for my bash script to process options and switches (both long --option and short -o forms).

I'd like to be able to trap invalid options and handle them, typically echoing out that the user should try cmd --help and then exiting the script.

Thing is, the invalid options are being caught by getopt, which is itself outputting a message such as "getopt: invalid option -- 'x'"

Here's the pattern I'm using to set my getopt parameters:

set -- $(getopt -o $SHORT_OPTIONS -l $LONG_OPTIONS -- "$@")

where both $LONG_OPTIONS and $SHORT_OPTIONS are a comma-delimited list of options.

Here's how I handle processing the options:

 while [ $# -gt 0 ]
    do
        case "$1" in
            -h|--help)
                cat <<END_HELP_OUTPUT

    Help
    ----

    Usage: ./cmd.sh 

    END_HELP_OUTPUT

                shift;
                exit
                ;;
            --opt1)
                FLAG1=true
                shift
                ;;
            --opt2)
                FLAG2=true
                shift
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "Option $1 is not a valid option."
                echo "Try './cmd.sh --help for more information."
                shift
                exit
                ;;
        esac
    done

getopt -q will suppress the output, but my trapping scheme within the case statement still fails to do what I expect. Instead, the program just executes, despite the invalid arguments.

like image 235
Tom Auger Avatar asked Oct 05 '11 15:10

Tom Auger


2 Answers

This sort of style works for me:

params="$(getopt -o d:h -l diff:,help --name "$cmdname" -- "$@")"

if [ $? -ne 0 ]
then
    usage
fi

eval set -- "$params"
unset params

while true
do
    case $1 in
        -d|--diff)
            diff_exec=(${2-})
            shift 2
            ;;
        -h|--help)
            usage
            exit
            ;;
        --)
            shift
            break
            ;;
        *)
            usage
            ;;
    esac
done
like image 83
l0b0 Avatar answered Oct 14 '22 04:10

l0b0


This is not the most robust solution, but it's reasonable; it relies on the following:

  • The error message that getopt prints is prefixed with "getopt: "
  • The assumption is that it's acceptable to pass through a cleaned-up version of getopt's error message, augmented with custom information.

Code snippet:

# Invoke getopt; suppress its stderr initially.
args=$(getopt -o $SHORT_OPTIONS -l $LONG_OPTIONS -- "$@" 2>/dev/null)
if [[ $? -ne 0 ]]; then # getopt reported failure
    # Rerun the same getopt command so we can capture stderr output *only* this time.
    # Inefficient (and a potential maintenance headache, if literals were involved), but this will only execute in case of invalid input.
    # Alternatively, redirect the first getopt invocation's stderr output to a temp. file and read it here.
    errmsg=$(getopt -o $SHORT_OPTIONS -l $LONG_OPTIONS -- "$@" 2>&1 1>&-)
    # Strip getopt's prefix and augment with custom information.
    echo -e "${errmsg#getopt: }\nTry './cmd.sh --help for more information." 1>&2
    exit 1
fi
like image 31
mklement0 Avatar answered Oct 14 '22 03:10

mklement0