Skip to content

Instantly share code, notes, and snippets.

@kaushalmodi
Created April 27, 2017 14:06
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kaushalmodi/74e9875d5ab0a2bc1010447f1bee5d0a to your computer and use it in GitHub Desktop.
Save kaushalmodi/74e9875d5ab0a2bc1010447f1bee5d0a to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# Time-stamp: <2017-04-27 09:57:21 kmodi>
#
# Example of using getopt to parse command line options
# http://stackoverflow.com/a/29754866/1219634 Limitation: All the options
# starting with - have to be listed in --options/--longoptions, else getopt will
# error out. So this cannot be used in wrapper scripts for other applications
# where you plan to pass on the non-wrapper-script options to that wrapped
# application.
# Initialize variables
help=0
debug=0
verbose=0
dummy_arg="__dummy__"
extra_args=("${dummy_arg}") # Because set -u does not allow undefined variables to be used
discard_opts_after_doubledash=0 # 1=Discard, 0=Save opts after -- to ${extra_args}
echo "All pre-getopt arguments: $*"
getopt --test > /dev/null
if [[ $? -ne 4 ]]; then
echo "I'm sorry, 'getopt --test' failed in this environment."
exit 1
fi
# An option followed by a single colon ':' means that it *needs* an argument.
# An option followed by double colons '::' means that its argument is optional.
# See `man getopt'.
SHORT=hDvo: # List all the short options
LONG=help,debug,verbose,output: # List all the long options
# - Temporarily store output to be able to check for errors.
# - Activate advanced mode getopt quoting e.g. via "--options".
# - Pass arguments only via -- "$@" to separate them correctly.
# - getopt auto-adds "--" at the end of ${PARSED}, which is then later set to
# "$@" using the set command.
PARSED=$(getopt --options ${SHORT} \
--longoptions ${LONG} \
--name "$0" \
-- "$@") #Pass all the args to this script to getopt
if [[ $? -ne 0 ]]; then
# e.g. $? == 1
# then getopt has complained about wrong arguments to stdout
exit 2
fi
# Use eval with "$PARSED" to properly handle the quoting
# The set command sets the list of arguments equal to ${PARSED}.
eval set -- "${PARSED}"
echo "Getopt parsed arguments: ${PARSED}"
echo "Effective arguments: $*"
echo "Num args: $#"
while [[ ( ${discard_opts_after_doubledash} -eq 1 ) || ( $# -gt 0 ) ]]
do
echo "parsing arg: $1"
case "$1" in
-o|--output) shift
out_file="$1";;
-h|--help) help=1;;
-D|--debug) debug=1;;
-v|--verbose) verbose=1;;
--) if [[ ${discard_opts_after_doubledash} -eq 1 ]]; then break; fi;;
*) extra_args=("${extra_args[@]}" "$1");;
esac
shift # Expose the next argument
done
# Now delete the ${dummy_arg} from ${extra_args[@]} array # http://stackoverflow.com/a/16861932/1219634
extra_args=("${extra_args[@]/${dummy_arg}}")
echo "help: ${help}, debug: ${debug}, verbose: ${verbose}, in: $1, out: $out_file, extra args=${extra_args[*]}"
@bobpaul
Copy link

bobpaul commented Mar 20, 2018

This puts all positional arguments, even those before the -- into extra args. The intention of in: $1 on the final line is unclear to me as $1 is always undefined by this point.

I've made an update to address these concerns, as well as some minor cleanup (like using declare for lists rather than dummy_arg).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment