echo '---------------- before shift -------------------'
echo ".. \$# : $#"
echo ".. \$@ : $@"
echo ".. \$* : $*"
echo '---------------- after shift -------------------'
opt=''
while [[ $# -gt 0 ]]; do
case "$1" in
-*) opt+="$1 "; shift;;
*) break ;;
esac
done
echo ".. \$# : $#"
echo ".. \$@ : $@"
echo ".. \$* : $*"
echo ".. \$opt : $opt"
if [[ 0 = "$#" ]]; then
echo -e "\033[0;33mERROR: must provide at least one non-opt param\033[0m"
exit 2
elif [[ 1 = "$#" ]]; then
path=''
params="$1"
else
path=${*: -1}
params=${*: 1:$#-1}
fi
echo '---------------- result -------------------'
echo ">> opt : ${opt}"
echo ">> params : ${params}"
echo ">> path : ${path}"
POSIX getopts Parser
[!NOTE]
β Pros: POSIX, built-in
β Cons: No long options, no multi-word values
while getopts "f:vd" opt; do
case $opt in
f ) FILE="$OPTARG" ;;
v ) VERBOSE=true ;;
d ) DRYRUN=true ;;
\? ) echo "Invalid option: -$OPTARG"; exit 1 ;;
esac
done
shift $((OPTIND -1))
# Reset in case getopts has been used previously in the shell.
OPTIND=1
output_file=''
verbose=0
while getopts "h?vf:" opt; do
case "$opt" in
h|\? ) show_help; exit 0 ;;
v ) verbose=1 ;;
f ) output_file=$OPTARG ;;
esac
done
shift $((OPTIND-1))
[ "${1:-}" = "--" ] && shift
echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"
GNU getopt Parser
[!NOTE]
MacOS: brew install gnu-getopt
β Pros: Short + long, nice user UX
β Cons: Not portable across BSD/macOS by default (GNU-only)
#!/usr/bin/env bash
ARGS=$(getopt -o f:vd -l file:,verbose,dry-run -- "$@")
eval set -- "$ARGS"
while true; do
case "$1" in
-f|--file ) FILE="$2" ; shift 2 ;;
-v|--verbose ) VERBOSE=true ; shift ;;
--dry-run ) DRYRUN=true ; shift ;;
-- ) shift ; break ;;
* ) break ;;
esac
done
# option --output/-o requires 1 argument
LONGOPTS=debug,force,output:,verbose
OPTIONS=dfo:v
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
# e.g. return value is 1
# then getopt has complained about wrong arguments to stdout
exit 2
fi
# read getoptβs output this way to handle the quoting right:
eval set -- "$PARSED"
d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
case "$1" in
-d|--debug ) d=y ; shift ;;
-f|--force ) f=y ; shift ;;
-v|--verbose ) v=y ; shift ;;
-o|--output ) outFile="$2" ; shift 2 ;;
-- ) shift ; break ;;
* ) echo "Programming error"; exit 3 ;;
esac
done
# handle non-option arguments
if [[ $# -ne 1 ]]; then
echo "$0: A single input file is required."
exit 4
fi
echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"
bash-argparse Library
[!NOTE]
β Pros: Clean, descriptive, built-in help
β Cons: Bash-only, external lib needed
# requires sourcing the library
source argparse.sh
argparse "$@" <<EOF
--file -f [arg] Path to the file
--verbose -v Enable verbose output
--dry-run Enable dry-run mode
EOF
# ARG_POSITIONAL_SINGLE([file], [File to process])
# ARG_OPTIONAL_BOOLEAN([verbose], [v], [Enable verbose])
# ARG_OPTIONAL_BOOLEAN([dry-run], [], [Dry run mode])
# ARG_HELP([Script to demonstrate argbash])
# ARGBASH_GO()
# The above is preprocessed by `argbash` into full Bash script