はじめに
getopts でできることをまとめておく。
ロングオプションはできないと書かれた記事があるけれど、できたよ。
こんなことができたら素敵
$ ./a.sh -h
usage: ./a.sh [OPTIONS...] ARGS...
options:
-v, --verbose
-h, --help
$ ./a.sh
option_verbose: 0
$ ./a.sh --verbose
option_verbose: 1
コードサンプル1
# !/bin/bash
set -eu
usage_exit() {
prog="./$(basename $0)"
echo_err "usage: $prog [OPTIONS...] ARGS..."
echo_err ""
echo_err "options:"
echo_err " -v, --verbose"
echo_err " -h, --help"
exit 1
}
echo_err() {
echo "$1" 1>&2
}
echo_err_badopt() {
local optarg="$1"
echo_err "$0: illegal option -- $optarg"
}
option_verbose=0
while getopts -- "-:hv" OPT; do
case $OPT in
-)
case $OPTARG in
help) usage_exit;;
verbose) option_verbose=$(($option_verbose+1));;
*) echo_err_badopt $OPTARG; usage_exit;;
esac;;
h) usage_exit;;
v) option_verbose=$(($option_verbose+1));;
\?) usage_exit;;
esac
done
shift $((OPTIND-1))
if [[ $# -ne 0 ]]; then
usage_exit
fi
echo "option_verbose: $option_verbose"
exit 0
コードサンプル2
先ほどのサンプルではもの足りない方はこちらを参照:
# !/bin/bash
set -eu
usage_exit() {
prog="./$(basename $0)"
echo_err "usage: $prog [OPTIONS...] ARGS..."
echo_err ""
echo_err "options:"
echo_err " --dry-run : Don't make any change (default)"
echo_err " -f, --force : Force mode"
echo_err " -l ITEM --list ITEM : Set list items"
echo_err " -V VAL --value VAL : Set value"
echo_err " -v, --verbose : Verbose mode"
exit 1
}
echo_err() {
echo "$1" 1>&2
}
echo_err_needarg() {
local optarg="$1"
echo_err "$0: option requires an argument -- $optarg"
}
echo_err_badopt() {
local optarg="$1"
echo_err "$0: illegal option -- $optarg"
}
option_dry_run=yes
option_list=()
option_value=''
option_verbose=0
while getopts -- "-:fhl:V:v" OPT; do
case $OPT in
-)
case $OPTARG in
dry-run)
option_dry_run=yes
;;
force)
option_dry_run=no
;;
list)
if [[ $OPTIND -gt $BASH_ARGC ]]; then
echo_err_needarg $OPTARG
usage_exit
fi
LONGOPT_ARG="${BASH_ARGV[$(($BASH_ARGC-$OPTIND))]}"
option_list+=($LONGOPT_ARG)
OPTIND=$((OPTIND+1))
;;
value)
if [[ $OPTIND -gt $BASH_ARGC ]]; then
echo_err_needarg $OPTARG
usage_exit
fi
LONGOPT_ARG="${BASH_ARGV[$(($BASH_ARGC-$OPTIND))]}"
option_value=$LONGOPT_ARG
OPTIND=$((OPTIND+1))
;;
verbose)
option_verbose=$(($option_verbose+1))
;;
*)
echo_err_badopt $OPTARG
usage_exit
;;
esac;;
f) option_dry_run=no;;
h) usage_exit;;
l) option_list+=($OPTARG);;
V) option_value=$OPTARG;;
v) option_verbose=$(($option_verbose+1));;
\?) usage_exit;;
esac
done
shift $((OPTIND-1))
cat <<__JSON__
{
"option_dry_run": "$option_dry_run",
"option_value": "$option_value",
"option_verbose": $option_verbose,
"option_list": [$(
if [[ ${#option_list[@]} -ne 0 ]]; then
for e in ${option_list[@]}; do
echo "\"$e\""
done | paste -sd,
fi
)],
"arguments": [$(
i=0
while [[ $# -gt 0 ]]; do
echo "\"$1\""
shift
done | paste -sd,
)]
}
__JSON__
その他
正直、動くけどロングオプションで引数は受けたくない。
bashでやれなくないけどやるの?、って感じ。
参考資料
- https://github.com/kumarstack55/sandbox/tree/master/bash/getopts
- getopts の '-:' は stackoverflow の記事を参照したがリンクが見つかってない。
-
bash によるオプション解析
- getopt, getopts, 自前で解析の比較を試みた投稿。ただし getopts でロングオプション指定は不可としている: