はじめに
getoptions はシェルスクリプト用のオプションパーサーです。getopts
や getopt
の代わりに使うことができ、getoptions
をインストールするだけで簡単にシェルスクリプトのオプション解析を実装することができます。しかし、不特定の人に配布するシェルスクリプトの場合は getoptions
をインストールしてもらうというのは選択肢にならないかもしれません。
でも大丈夫! getoptions はオプションパーサーのジェネレータとして使うこともできます。ジェネレータとして使うと自分でオプションパーサーのコードを書く必要はありません。
オプションパーサーとしての使い方はこちら
「シェルスクリプト(bash等)の引数解析が究極的に簡単になりました」
使い方
オプションパーサーとして使う場合は getoptions
コマンドをインストールするだけで使えますが、ジェネレーターとして使う場合は gengetoptions
コマンドも必要になります。もちろんコードの生成に使用するだけなのでシェルスクリプトの配布先にはどちらもインストールする必要はありません。(ダウンロードはこちらから)
まずオプション解析を行いたいシェルスクリプトに、オプションの定義を書いた関数(parser_definition
)とそれを認識するタグ(@getoptions
~ @end
)、そしてオプションパーサーを埋め込むためのタグ(@gengetoptions parser -i parser_definition -
~ @end
)を書きます。@gengetoptions
の引数は gengetoptions
の引数と同じです。
#!/bin/sh
set -eu
# @getoptions
parser_definition() {
setup REST help:usage -- "Usage: example.sh [options]... [arguments]..." ''
msg -- 'Options:'
flag FLAG -f --flag -- "takes no arguments"
param PARAM -p --param -- "takes one argument"
option OPTION -o --option on:"default" -- "takes one optional argument"
disp :usage -h --help
disp VERSION --version
}
# @end
# @gengetoptions parser -i parser_definition -
# @end
# ここからメイン処理
echo "FLAG: $FLAG, PARAM: $PARAM, OPTION: $OPTION"
printf '%s\n' "$@" # rest arguments
そして以下のコマンドを実行すると、@gengetoptions
~ @end
の中にオプションパーサーが生成されます。コマンドを実行するたびに @gengetoptions
~ @end
の間のコードは上書きされるので注意してください。
gengetoptions embed --overwrite example.sh
生成コード
#!/bin/sh
set -eu
# @getoptions
parser_definition() {
setup REST help:usage -- "Usage: example.sh [options]... [arguments]..." ''
msg -- 'Options:'
flag FLAG -f --flag -- "takes no arguments"
param PARAM -p --param -- "takes one argument"
option OPTION -o --option on:"default" -- "takes one optional argument"
disp :usage -h --help
disp VERSION --version
}
# @end
# @gengetoptions parser -i parser_definition -
# Generated by getoptions (BEGIN)
# URL: https://github.com/ko1nksm/getoptions (v3.3.0)
FLAG=''
PARAM=''
OPTION=''
REST=''
getoptions_parse() {
OPTIND=$(($#+1))
while OPTARG= && [ $# -gt 0 ]; do
case $1 in
--?*=*) OPTARG=$1; shift
eval 'set -- "${OPTARG%%\=*}" "${OPTARG#*\=}"' ${1+'"$@"'}
;;
--no-*|--without-*) unset OPTARG ;;
-[po]?*) OPTARG=$1; shift
eval 'set -- "${OPTARG%"${OPTARG#??}"}" "${OPTARG#??}"' ${1+'"$@"'}
;;
-[fh]?*) OPTARG=$1; shift
eval 'set -- "${OPTARG%"${OPTARG#??}"}" -"${OPTARG#??}"' ${1+'"$@"'}
OPTARG= ;;
esac
case $1 in
'-f'|'--flag')
[ "${OPTARG:-}" ] && OPTARG=${OPTARG#*\=} && set "noarg" "$1" && break
eval '[ ${OPTARG+x} ] &&:' && OPTARG='1' || OPTARG=''
FLAG="$OPTARG"
;;
'-p'|'--param')
[ $# -le 1 ] && set "required" "$1" && break
OPTARG=$2
PARAM="$OPTARG"
shift ;;
'-o'|'--option')
set -- "$1" "$@"
[ ${OPTARG+x} ] && {
case $1 in --no-*|--without-*) set "noarg" "${1%%\=*}"; break; esac
[ "${OPTARG:-}" ] && { shift; OPTARG=$2; } || OPTARG='default'
} || OPTARG=''
OPTION="$OPTARG"
shift ;;
'-h'|'--help')
usage
exit 0 ;;
'--version')
echo "${VERSION}"
exit 0 ;;
--)
shift
while [ $# -gt 0 ]; do
REST="${REST} \"\${$(($OPTIND-$#))}\""
shift
done
break ;;
[-]?*) set "unknown" "$1"; break ;;
*)
REST="${REST} \"\${$(($OPTIND-$#))}\""
esac
shift
done
[ $# -eq 0 ] && { OPTIND=1; unset OPTARG; return 0; }
case $1 in
unknown) set "Unrecognized option: $2" "$@" ;;
noarg) set "Does not allow an argument: $2" "$@" ;;
required) set "Requires an argument: $2" "$@" ;;
pattern:*) set "Does not match the pattern (${1#*:}): $2" "$@" ;;
notcmd) set "Not a command: $2" "$@" ;;
*) set "Validation error ($1): $2" "$@"
esac
echo "$1" >&2
exit 1
}
usage() {
cat<<'GETOPTIONSHERE'
Usage: example.sh [options]... [arguments]...
Options:
-f, --flag takes no arguments
-p, --param PARAM takes one argument
-o, --option[=OPTION] takes one optional argument
-h, --help
--version
GETOPTIONSHERE
}
eval getoptions_parse ${1+'"$@"'}; eval set -- "${REST}"
# Generated by getoptions (END)
# @end
# ここからメイン処理
echo "FLAG: $FLAG, PARAM: $PARAM, OPTION: $OPTION"
printf '%s\n' "$@" # rest arguments
生成されるコードは見ての通り比較的シンプルなコードです(若干汎用的なコードだが手動で書くのと大差ないはず)。ヘルプを表示するときに cat
コマンドを呼び出すだけで、オプションの解析に外部コマンドは一切使わないため高速です。単一のコードで bash だけではなく dash などを含め POSIX シェル全てに対応しているコードを生成します。オプションを変更するときは parser_definition
関数の中身を変更してもう一度生成するだけです。
個人的なスクリプト等は getoptions
コマンドをインストールしておいて動的にオプションパーサーを生成する方がメンテナンスが楽で推奨する使い方ですが、このようにジェネレータとして使用することもできるようになっています。
さいごに
一つヒントとして生成されたコードも getoptions 自体もライセンスは CC0 でパブリックドメイン(著作権は主張しない)相当であるため、生成されたコードを元にあたかも getoptions を使っていないかのように書き換えて配布することも、独自のライセンスで配布するのも完全に自由です。最初からそのような使い方を想定しています。生成されたコードに含まれる URL はメンテナンスする際の参考情報に過ぎず --no-comments
オプションを @gengetoptions
タグに追加すれば生成しないようにすることもできます。
もういいかげんシェルスクリプトのオプション解析コードを試行錯誤して自分で書くのはやめませんか?