Parsing command line arguments on the shell is often done in an ad-hoc fashion, growing unwieldy as time goes by, but there are tools to make that elegant. Here’s a complete example.
I use this in the conf [1] project (easy setup of autotools projects). It builds on the great solution by Adam Katz [2].
# outer loop to allow processing option arguments at the end while test ! $# -eq 0; do # getopts loop, here you define the short options: # h for -h, l: for -l <lang>. -: provides support for long-options. while getopts -- hl:-: arg "$@"; do case $arg in h ) ARG_HELP=true ;; l ) ARG_LANG="$OPTARG" ;; - ) LONG_OPTARG="${OPTARG#*=}" case "$OPTARG" in help ) ARG_HELP=true;; lang=?* ) ARG_LANG="$LONG_OPTARG" ;; # FIXME: using the same option twice (either both # after the argument or both before it) gives the # first, not the second value lang* ) ARG_LANG="${@:$OPTIND:1}" ; OPTIND=$(($OPTIND + 1));; vcs=?* ) ARG_VCS="$LONG_OPTARG" ;; vcs* ) ARG_VCS="${@:$OPTIND:1}" ; OPTIND=$(($OPTIND + 1));; '' ) break ;; # "--" terminates argument # processing to allow giving # options for autogen.sh after # -- * ) echo "Illegal option --$OPTARG" >&2; exit 2;; esac;; \? ) exit 2 ;; # getopts already reported the illegal # option esac done shift $((OPTIND-1)) # remove parsed options and args from $@ list # reinitialize OPTIND to allow parsing again OPTIND=1 # provide help output. if test x"${ARG_HELP}" = x"true"; then echo "${PROG} new [-h | --help] [-l | --lang <LANGUAGE>] [--vcs <VCS>] PROJECT_NAME" exit 0 fi # get the argument if test x"${1}" = x"--"; then if test x"${PROJ}" = x""; then echo "Missing project name." >&2; exit 2 else # nothing more to parse. # Remove -- from the remaining arguments shift 1 break fi fi if test ! x"${1}" = x""; then PROJ="${1%/}" # without trailing slash fi # remove the argument, then continue the loop to allow putting # the options after the argument shift 1 done
Additional explanation for this is available from Adam Katz (2015) [3]. I’m allowed to include it here, because every answer on Stackoverflow is licensed under creativecommons attribution sharealike (cc by-sa) [4] and because cc by-sa is upwards compatible [5] to GPLv3 [6].
# From Adam Katz, 2015: http://stackoverflow.com/users/519360/adam-katz # Available at http://stackoverflow.com/a/28466267/7666 # License: cc by-sa: https://creativecommons.org/licenses/by-sa/3.0/ while getopts ab:c-: arg; do case $arg in a ) ARG_A=true ;; b ) ARG_B="$OPTARG" ;; c ) ARG_C=true ;; - ) LONG_OPTARG="${OPTARG#*=}" case $OPTARG in alpha ) ARG_A=true ;; bravo=?* ) ARG_B="$LONG_OPTARG" ;; bravo* ) echo "No arg for --$OPTARG option" >&2; exit 2 ;; charlie ) ARG_C=true ;; alpha* | charlie* ) echo "No arg allowed for --$OPTARG option" >&2; exit 2 ;; '' ) break ;; # "--" terminates argument processing * ) echo "Illegal option --$OPTARG" >&2; exit 2 ;; esac ;; \? ) exit 2 ;; # getopts already reported the illegal option esac done shift $((OPTIND-1)) # remove parsed options and args from $@ list
With this and with the practical usage at the top you should be able to implement clean commandline parsing with ease.
Happy Hacking!
Links:
[1] https://bitbucket.org/ArneBab/conf/
[2] http://stackoverflow.com/a/28466267/7666
[3] http://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options/28466267#28466267
[4] https://creativecommons.org/licenses/by-sa/4.0/
[5] http://www.draketo.de/english/free-software/by-sa-gpl
[6] http://gnu.org/l/gpl