シェル関数が定義されているこんなファイルsourceme.shがありました:
# sourceme.sh getopts_in_function_nogood() { echo ">>getopts_in_function_nogood" server= while getopts "ns:" opt; do case $opt in n) echo "dry run";; s) server=$OPTARG;; ?) echo "unknown option" 1>&2; return;; esac done shift $(($OPTIND - 1)) echo "server=$server" }
新しいターミナルを立ち上げて、ロードして関数を実行してみます。
goa[~]$ . sourceme.sh goa[~]$ getopts_in_function_nogood -n -s web >>getopts_in_function_nogood dry run server=web
期待通りの動作です。が、続けてもいっかい同じのを実行してみると…
goa[~]$ !! getopts_in_function_nogood -n -s web >>getopts_in_function_nogood server=
なんということでしょう!!! オプションが全く解釈されてません!!!!! 同じなのに!! なぞい!!!
で、man bashを読んでみるとこんな記述が。。。
OPTIND はシェルまたはシェルスクリプトが呼び出される度に 1 に初期化されます。オプションが引き数を必要とする場合には、 getopts はその引き数を変 数OPTARG に格納します。シェルが OPTIND を自動的に再設定することはありません。 1 つのシェルが呼び出されている間に別のパラメータの組合せを使う場合には、 getopts の呼び出しの間に手動で再設定を行わなければなりません。
(ꏿ⌓ꏿ)
ということで、シェル関数でgetoptsを使うときはOPTINDを明に初期化しましょう。
getopts_in_function_good() { echo ">>getopts_in_function_good" server= OPTIND_OLD=$OPTIND OPTIND=1 while getopts "ns:" opt; do case $opt in n) echo "dry run";; s) server=$OPTARG;; ?) echo "unknown option" 1>&2; return;; esac done shift $(($OPTIND - 1)) OPTIND=$OPTIND_OLD echo "server=$server" }
というわけで早速マイyasnippetのsh-mode/getoptsを更新しましたとさ。めでたしめでたし。