Edited at

getoptsがどのように動作しているのか


はじめに

最近、シェルスクリプトを組む機会が多くgetoptsを使ってオプション解析をしてはいたのですが、なんとなくで使っていました。

ですが今回、getoptsの動作を調べてみて目から鱗が落ちたので、書き残しておきます。

また、getoptsを使ったオプション解析の動作についても説明を書きました。

なぜ、あのコードで動くのだろうと思っている方はぜひ読んで下さい。


getoptsの動作

まずは、getoptsの動作から説明します。


基本動作


my_command.sh

#!/bin/bash

getopts abc opt
echo "オプション: $opt"

これに実行権限を与えて./my_command.sh -aを実行すると、

オプション: a

と表示されます。

getoptsを使うと、コマンドの引数に与えられたものが、getoptsの第一引数に含まれていた場合、第二引数に代入されます。

上のスクリプトの場合、my_command.shに与えられた引数が-aだったため、aという文字がoptという変数に代入されました。


オプション以外のものが渡された場合

では./my_command.sh -zの場合はどうでしょうか。

この場合、zという文字はgetoptsの第一引数abcの中に含まれていません。なので、getoptsはエラーメッセージを吐き、?という文字がoptに代入されます。ただし、終了ステータスは0 (正常終了) になります。

下が実行結果。

./my_command.sh: illegal option -- z

オプション: ?

一行目のエラーメッセージを表示させたくないときは、getopts abc opt > /dev/null 2>&1などとして、メッセージを捨てて下さい。


オプションが2つ渡された場合

オプションが2つ渡された場合はどうでしょう。

./my_command.sh -a -b

このコマンドを実行した結果は下のとおり。

オプション: a

aのみを渡したときと変わっていません。なぜならgetoptsは1つずつしかオプションを読み込めないからです。

2つとも読み込むためには下のように、getoptsを2回実行する必要があります。


my_command.sh

#!/bin/bash

getopts abc opt
echo "オプション: $opt"
getopts abc opt
echo "オプション: $opt"

./my_command.sh -a -bを実行します。

オプション: a

オプション: b

このとき、渡すオプションを-abとしても、実行結果は変わりません。

ちゃんとオプションを読み込めていることがわかります。


オプションが渡されなかった場合

上のほうで、コマンドに渡すオプションがgetopts第一引数に入っていなくても、正常終了すると書きました。

なので、


my_command.sh

#!/bin/bash

getopts abc opt > /dev/null 2>&1
echo "ステータスコード: $?"
echo "オプション: $opt"

というコマンドを./my_command.sh -zで実行すると、

ステータスコード: 0

オプション: ?

と表示されます。

一方で、オプションを渡さなかった (./my_command.shのみを実行した)場合、

ステータスコード: 1

オプション: ?

になり、異常終了となります。


whileと組み合わせる

getoptsを使ってオプション解析をしているqiitaの記事を見てみる12と、whileを使って行っているものが見られます。

こんな感じ。


my_command.sh

while getopts abc opt

do
case
$opt in
# オプションの処理
esac
done

なぜこれが動くのでしょうか。

シェルスクリプトの動作がわかっていれば当たり前なことかもしれませんが解説します。


whileの動作

while文は下のような形を取ります。

while コマンド

do
処理
done

そして、コマンドの終了ステータスが異常終了だった場合、while文を抜けるという動作になっています。


whileとgetoptsを組み合わせる。

ここで、先ほどのgetoptsを使ったwhile文を考えてみましょう。


my_command.sh

while getopts abc opt

do
case
$opt in
# オプションの処理
esac
done

whileの後にgetopts abc optとあるので、getoptsが異常終了するまでオプションの読み込みを繰り返しています。

例えば、./my_command.sh -abzとして実行した場合、optという変数には、abzという文字が順番に入ってdo ~ case文が実行されます。

そして、getoptsの読み込めるオプションがなくなったとき、getoptsは異常終了するので、whileを抜けます。

以上の動作によって、オプションの解析ができるというわけです。

まとめると、下のようなスクリプトを書けば、コマンドのオプション解析をすることができるのでした。

#!/bin/bash

usage() {
echo "Usage: $(basename $0) [-a] [-b] [-c]"
exit 1
}

while getopts abch opt
do
case
$opt in
a ) echo "option a" ;; # aのときの処理
b ) echo "option b" ;; # bのときの処理
c ) echo "option c" ;; # cのときの処理
h ) usage ;; # h: ヘルプを表示
\? ) usage ;; # ?: オプションがgetoptsの第一引数に含まれていない場合
esac
done

shift $((OPTIND - 1))


おわりに

getoptsを使ってコマンドを作れるとかっこいいので、みなさんも作ってみてください。