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_开发者_StackOverflow社区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.
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
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
Do you have to use getopt at all? If you just use
while [ $# -gt 0 ]; do
case "$1" in
-d|--diff)
diff_exec=(${2-})
shift
;;
-h|--help)
usage
exit
;;
--)
break
;;
*)
usage
;;
esac
shift
done
Then you own code is doing the checking.
I found this to work as the last item in the getopts case statement:
*) eval echo "Unrecognized arg \$$[OPTIND-1]"; usage; exit ;;
I'm not sure if this can help, but getopt(1) uses getopt(3) and if I recall correctly getopt(3) suppress error reporting if the fist character of the OPTSTRING is a colon.
Here is a command line parsing I have used. It could be improved with more parsing logic to handle missing options and parameters.
For the command line: -a AA -b BB -c CC, the result s/b a=AA b=BB c=CC
OPT=( "$@" ) # Parses the command line into words.
for [[ I=0;I<${#OPT[@]};I++ ]]
do
case "${OPT[$I]}" in
-a) a=${OPT[$I+1]} ;;
-b) b=${OPT[$I+1]} ;;
-c) c=${OPT[$I+1]} ;;
esac
done
精彩评论