だいぶ前の話、Perlを使わないでShellスクリプトで頑張っていた頃、Shellでコマンドラインオプションの解析をやる時は
#!/bin/sh
for OPT in $*
do
case $OPT in
'-x' )
FLAG_X="TRUE"
;;
'-y' )
shift
FLAG_Y="TRUE"
VALUE_Y=$1
;;
esac
shift
done
if [ "$FLAG_X" = "TRUE" ]; then
echo "Option -x specified."
fi
if [ "$FLAG_Y" = "TRUE" ]; then
echo "Option -y $VALUE_Y specified."
fi
という感じで $* と shift を使ってやっていたのですが、最近 getopts なる素敵なビルトインコマンドを知りました。これを使うと上のスクリプトは下のように書き直すことができます。
#!/bin/sh
while getopts xy: OPT
do
case $OPT in
"x" )
FLAG_X="TRUE"
;;
"y" )
FLAG_Y="TRUE"
VALUE_Y=$OPTARG
;;
esac
done
if [ "$FLAG_X" = "TRUE" ]; then
echo "Option -x specified."
fi
if [ "$FLAG_Y" = "TRUE" ]; then
echo "Option -y $VALUE_Y specified."
fi
スクリプトの行数自体はあんまり変わらないように見えますが、shift 忘れをよくやらかしていたので、それが無くなった分つまらないバグを入れ込まなくなった気がします。
getoptsの解説
getopts の引数にオプションの文字を指定しますが、文字のあとに : (コロン)をつけると、引数ありのオプションという扱いになります。さらに
$ ./getopts.sh -z
のように未定義のオプションを指定すると
/home/oinume/tmp/script/getopts.sh: illegal option -- z
というように怒られます。このエラーハンドリングを自前でやるには :xy: のように、getopts の引数の文字列の最初を : にすればいいみたい。
さらに $OPTIND という変数を下記のように -1 してやることで、オプション以降に与えられた引数を取得することができます。例えば、上のスクリプトに下記を足して
shift `expr $OPTIND - 1`
if [ -n "$1" ]; then
echo "Argument $1 specified."
fi
$ ~/tmp/script/getopts.sh -x -y yyy hoge
のように実行すると
Option -x specified.
Option -y yyy specified.
Argument hoge specified.
となります。getopts を実行すると、$OPTIND にはオプションのインデックス番号が保存されているので、これを -1 して shift してやることで、オプション以降の引数が取得できるようになるという仕組みです。
最近はこういうレベルのプログラムだったらPerlで書くことも多かったのですが、Shell でも十分いけそうだと実感しました。