After several days of research, I still can\'t figure out the best method for parsing cmdline args in a .sh script. According to my references the getopts cmd is the way to
If you want to avoid using getopt
you can use this nice quick approach:
##
comments (customise as you wish).log.sh
#!/bin/sh
## $PROG 1.0 - Print logs [2017-10-01]
## Compatible with bash and dash/POSIX
##
## Usage: $PROG [OPTION...] [COMMAND]...
## Options:
## -i, --log-info Set log level to info (default)
## -q, --log-quiet Set log level to quiet
## -l, --log MESSAGE Log a message
## Commands:
## -h, --help Displays this help and exists
## -v, --version Displays output version and exists
## Examples:
## $PROG -i myscrip-simple.sh > myscript-full.sh
## $PROG -r myscrip-full.sh > myscript-simple.sh
PROG=${0##*/}
LOG=info
die() { echo $@ >&2; exit 2; }
log_info() {
LOG=info
}
log_quiet() {
LOG=quiet
}
log() {
[ $LOG = info ] && echo "$1"; return 1 ## number of args used
}
help() {
grep "^##" "$0" | sed -e "s/^...//" -e "s/\$PROG/$PROG/g"; exit 0
}
version() {
help | head -1
}
[ $# = 0 ] && help
while [ $# -gt 0 ]; do
CMD=$(grep -m 1 -Po "^## *$1, --\K[^= ]*|^##.* --\K${1#--}(?:[= ])" log.sh | sed -e "s/-/_/g")
if [ -z "$CMD" ]; then echo "ERROR: Command '$1' not supported"; exit 1; fi
shift; eval "$CMD" $@ || shift $? 2> /dev/null
done
Running this command:
./log.sh --log yep --log-quiet -l nop -i -l yes
Produces this output:
yep
yes
By the way: It's compatible with posix!
I find the use of getopt
to be the easiest. It provides correct handling of arguments which is tricky otherwise. For example, getopt
will know how to handle arguments to a long option specified on the command line as --arg=option
or --arg option
.
What is useful in parsing any input passed to a shell script is the use of the "$@"
variables. See the bash man page for how this differs from $@
. It ensures that you can process arguments that include spaces.
Here's an example of how I might write s script to parse some simple command line arguments:
#!/bin/bash
args=$(getopt -l "searchpath:" -o "s:h" -- "$@")
eval set -- "$args"
while [ $# -ge 1 ]; do
case "$1" in
--)
# No more options left.
shift
break
;;
-s|--searchpath)
searchpath="$2"
shift
;;
-h)
echo "Display some help"
exit 0
;;
esac
shift
done
echo "searchpath: $searchpath"
echo "remaining args: $*"
And used like this to show that spaces and quotes are preserved:
user@machine:~/bin$ ./getopt_test --searchpath "File with spaces and \"quotes\"."
searchpath: File with spaces and "quotes".
remaining args: other args
Some basic information about the use of getopt
can be found here