bash でオプション解析
シェルスクリプトでコマンドラインのオプションを解析するために、自分用の ロジックを実装してみた。
いつもは Posix shell の範囲で作るのだが、今回は bash 用。 配列が使えると何かと便利そうなので。
まあ、脱 bash も後でやるかも。
問題は
コマンドラインから取得されるオプションの解析。
C 言語なら、getopt.3にある、getopt() や getopt_long() の例を使えば良い。
ところがシェルスクリプトだと、イマイチ定番の手法が確立されていない 模様。
このページのスクリプトにちょっと追加して、まぁまぁ、使えるものが できたので、自分用のテンプレとする事にした。
検索してみる
前出の bash によるオプション解析 ページが詳しい。
手法としては 3 つある。
で、このページの一番下に出てくる以下を元にする事にした。
declare -i argc=0
declare -a argv=()
while (( $# > 0 ))
do
case "$1" in
-*)
if [[ "$1" =~ 'n' ]]; then
nflag='-n'
fi
if [[ "$1" =~ 'l' ]]; then
lflag='-l'
fi
if [[ "$1" =~ 'p' ]]; then
pflag='-p'
fi
shift
;;
*)
((++argc))
argv=("${argv[@]}" "$1")
shift
;;
esac
done
改良してみる
上記で不満だったのは、-nl なんかが使える一方、-x とか、対象外のオプションを 指定されたときに検出できないこと。
また、オプションの形式も -o XXX、-oXXX 、–option XXX に加えて –option=XXX も 欲しいなぁ、と思った。
結果、以下の様になった。
declare -i argc=0
declare -a argv=()
while (( $# > 0 )); do
case "$1" in
'--' | '-' ) # <-- (1)
shift
((argc+=$#))
argv=("${argv[@]}" "$@")
break
;;
'-o' | '--option' ) # <-- (2)
if _isValue "$2" ; then
arg_o="$2"
shift
else
_usage
fi
;;
--option=* ) # <-- (2)
arg_o="${1#--option=}"
;;
-o* ) # <-- (2)
arg_o="${1#-o}"
;;
-* )
if ! getopt ":lnp" "$1" 2>&1 >/dev/null ; then # <-- (3)
_usage
fi
if [[ "$1" =~ 'l' ]]; then
arg_l="true"
fi
if [[ "$1" =~ 'n' ]]; then
arg_n="true"
fi
if [[ "$1" =~ 'p' ]]; then
arg_p="true"
fi
;;
* )
((++argc))
argv=("${argv[@]}" "$1")
;;
esac
shift
done
スクリプト中の # <– (n) がポイント
以下、ポイント毎に説明する
--
や-
でオプション解析を中断し、以後の引数をまとめる- -o XXX 、-oXXX 、–opton XXX 、–opton=XXX の解析。くっついている場合先頭の
-o
や--option=
を削除して取り込む - -l 、-n 、-p を -np などと指定しつつ、”:lnp” でそれ以外をエラーにする
解析終了後にはオプション(-で始まる引数)以外の引数が argv に入り、 その個数が argc に入る。
ちなみに _usage は使用法を表示して exit 1 する関数。
_isValue は後続の引数がオプション(-
で始まる)であるか
調べる関数で、以下の様になる。
function _isValue() {
local opt
opt="$1"
if [[ -z "$opt" ]] || [[ "$opt" =~ ^-+ ]]; then
return 1
fi
return 0
}
まとめ
一個のオプションである、-o XXX、-oXXX 、–option XXX 、–option=XXX を 解析するのに 3 個も case のターゲットを記述するのは冗長にもなるし、 同じ処理を複数の場所に書かなければならないのも悩ましい。また、 全体が長くなるのも避けられない。が、それらを割り切れれば、まぁまぁ 良いんじゃないかな、と思う。
記述を少しでも短くし、読みやすくするために、共通処理は関数化するなどして、 記述量を減らすのも大事。