This is a little tutorial that I found in my search to learn how to use getopt
(mind: not getopts
, which is a completely different thing). I want to share it here because I find it refreshingly to the point. Just the main code block already tells almost the whole story:
#!/bin/bash
# Set some default values:
ALPHA=unset
BETA=unset
CHARLIE=unset
DELTA=unset
usage()
{
echo "Usage: alphabet [ -a | --alpha ] [ -b | --beta ]
[ -c | --charlie CHARLIE ]
[ -d | --delta DELTA ] filename(s)"
exit 2
}
PARSED_ARGUMENTS=$(getopt -a -n alphabet -o abc:d: --long alpha,bravo,charlie:,delta: -- "$@")
VALID_ARGUMENTS=$?
if [ "$VALID_ARGUMENTS" != "0" ]; then
usage
fi
echo "PARSED_ARGUMENTS is $PARSED_ARGUMENTS"
eval set -- "$PARSED_ARGUMENTS"
while :
do
case "$1" in
-a | --alpha) ALPHA=1 ; shift ;;
-b | --beta) BETA=1 ; shift ;;
-c | --charlie) CHARLIE="$2" ; shift 2 ;;
-d | --delta) DELTA="$2" ; shift 2 ;;
# -- means the end of the arguments; drop this, and break out of the while loop
--) shift; break ;;
# If invalid options were passed, then getopt should have reported an error,
# which we checked as VALID_ARGUMENTS when getopt was called...
*) echo "Unexpected option: $1 - this should not happen."
usage ;;
esac
done
echo "ALPHA : $ALPHA"
echo "BETA : $BETA "
echo "CHARLIE : $CHARLIE"
echo "DELTA : $DELTA"
echo "Parameters remaining are: $@"
Just be sure to correct the inadvertent mixing of beta
and bravo
.
Correction regarding the article: as far as I know, GNU doesn’t have a
getopt
utility; the version on most Linux OSes comes from util-linux.Note that other
getopt
implementations have different features and some are simply broken. For example, BSDgetopt
doesn’t support long options and comes with this known bug:Arguments containing white space or embedded shell metacharacters generally will not survive intact; this looks easy to fix but is not.
For cross-platform scripts it’s probably best to use the
getopts
shell builtin instead, the downside being it only supports fairly basic (POSIX) syntax even on Bash.I’ve got one from stackoverflow i tidied up and expanded, that handles long options as well, but you really should learn Python instead. Talking from experience; even with a state machine approach and helpful little functions and debug helpers, juggling so much input is hard in shell.
Well, you’re warned now and here it is:
tool_name="tool" tool_descr="tool does tool things" printhelp() { printf '%s\nSyntax: %s [options]...\n%s\n' "$tool_descr" "$tool_name" "$tool_args" } bool() { [ -n "$OPTARG" ] && error "--${OPT} accepts no parameters" 7; } # cli-parser #-------------------------------------------------------------- tool_args=' -s, --stuff stuff to do -o, --other other stuff to do -V, --verbose Tell what i do -v, --version Print version of this tool and exit -h, --help This help text ' while getopts "vVhbs:o:-:" OPT; do # support long options: https://stackoverflow.com/a/28466267/519360 if [ "$OPT" = "-" ]; then # long option: reformulate OPT and OPTARG OPT="${OPTARG%%=*}" # extract long option name OPTARG="${OPTARG#"$OPT"}" # extract long option argument (may be empty) OPTARG="${OPTARG#=}" # if long option argument, remove assigning `=` fi case "$OPT" in s | stuff ) arg_stuff="$OPTARG" ;; o | other ) arg_other="$OPTARG" ;; V | verbose ) bool; verbose=true ;; v | version ) bool; printversion; exit 0 ;; h | help ) printhelp; exit 0 ;; ??* ) error "Illegal option --$OPT" 22 # bad long option ;; : ) exit 2 # missing parameter (error reported via getopts) ;; ? ) exit 22 # bad short option (error reported via getopts) esac done [ $OPTIND -eq 1 ] && \ if { [ -e "$1" ] && [ $# -gt 0 ]; } # check for lazy file argument then lazy_arg="$1" else printhelp && exit 0 fi shift $((OPTIND-1)) # remove parsed options and args from $@ list
There is also the manual way, I am not saying it’s perfect but sometimes it offers better alternative than getopt.
https://gist.github.com/dgoguerra/9206418I still think my favorite shell, NuShell, handles this the best:
def alphabet [ --alpha (-a) --charlie (-c): string # comments on these lines are parsed and used for automatically generated --help flag --delta: int ...filenames: string ] { echo \$alpha }"
So much less boilerplate
If you find yourself needing this, the correct thing to do is to stop writing that shell scripts and switch to a proper language.
While I agree with you, I think it’s a bit unkind to reply in this way. Sometimes bash is all you have, and it’s interesting to learn about tools even if you’re unlikely to use them.