case文

前回、一対多の判定の場合は「if」文ではなく、「case」文を使うべきだと書きましたが、
今回はそれを実証してみたいと思います。

今回からはスクリプトの要件を以下に変えてみます。

・第一オプションが「Windows」の場合、「Hello Bill Gates!!」と表示する。
・第一オプションが「Linux」の場合、「Hello Linus!!」と表示する。
・第一オプションが「OpenBSD」の場合、「Hello Theo!!」と表示する。
・第一オプションが「Solaris」の場合、「Hello Bill Joy!!」と表示する。
・第一オプションが「emacs」の場合、「Hello RMS!!」と表示する。
・第一オプションが「sendmail」の場合、「Hello Eric!!」と表示する。
・第一オプションが上記以外の場合、「Hello World!!」と表示する。

※若干、Hello Worldから趣旨が外れてきてる気がしないでもないですが、まぁいいでしょう。

まず、前回書いたスクリプトを改変します。
前回と同じく、「elif」文で選択肢を増やす形です。
エディタで「helloworld4.sh」を作成し、以下を記述してみましょう。

#!/bin/bash 
## 変数初期化
V_OPTION=$1     ## 第一オプションを代入

## オプション判定
## 第一オプションが「Windows」の場合、「Hello Bill Gates!!」と出力
## 第一オプションが「Linux」の場合、「Hello Linus!!」と出力
## 第一オプションが「OpenBSD」の場合、「Hello Theo!!」と出力
## 第一オプションが「Solaris」の場合、「Hello Bill Joy!!」と出力
## 第一オプションが「emacs」の場合、「Hello RMS!!」と出力
## 第一オプションが「sendmail」の場合、「Hello Eric!!」と出力
## 第一オプションが上記以外の場合、「Hello World!!」と出力

if [ "${V_OPTION}" = "Windows" ]
then
    echo "Hello Bill Gates!!"
elif [ "${V_OPTION}" = "Linux" ]
then
    echo "Hello Linus!!"
elif [ "${V_OPTION}" = "OpenBSD" ]
then
    echo "Hello Theo!!"
elif [ "${V_OPTION}" = "Solaris" ]
then
    echo "Hello Bill Joy!!"
elif [ "${V_OPTION}" = "emacs" ]
then
    echo "Hello RMS!!"
elif [ "${V_OPTION}" = "sendmail" ]
then
    echo "Hello Eric!!"
else
    echo "Hello World!!"
fi

実行結果はこんな感じです。

# ./helloworld4.sh Windows
Hello Bill Gates!!
# ./helloworld4.sh Linux
Hello Linus!!
# ./helloworld4.sh OpenBSD
Hello Theo!!
# ./helloworld4.sh Solaris
Hello Bill Joy!!
# ./helloworld4.sh emacs
Hello RMS!!
# ./helloworld4.sh sendmail
Hello Eric!!
# ./helloworld4.sh
Hello World!!
# 

実行結果は想定通りです。

続いて、「case」文を使った実装です。
「case」文は、以下の様な構文で記述します。

case 判定対象変数 in
    判定条件1)
        判定条件1の処理内容
        ;;
    判定条件2)
        判定条件2の処理内容
        ;;
    *) #例外処理
        例外処理内容
        ;;
esac

一つの判定対象に対して、複数の判定条件を記述していきます。
字下げにもよりますが、パッと見た目でも、判定条件毎の処理が判りやすいです。
また判定項目が増えた際も、判定対象は変わりませんので、判定条件を増やすだけで対応できます。

では、新たに「helloworld5.sh」を作成し、以下を記述してみましょう。

#!/bin/bash 
## 変数初期化
V_OPTION=$1     ## 第一オプションを代入


## オプション判定
## 第一オプションが「Windows」の場合、「Hello Bill Gates!!」と出力
## 第一オプションが「Linux」の場合、「Hello Linus!!」と出力
## 第一オプションが「OpenBSD」の場合、「Hello Theo!!」と出力
## 第一オプションが「Solaris」の場合、「Hello Bill Joy!!」と出力
## 第一オプションが「emacs」の場合、「Hello RMS!!」と出力
## 第一オプションが「sendmail」の場合、「Hello Eric!!」と出力
## 第一オプションが上記以外の場合、「Hello World!!」と出力

case "${V_OPTION}" in
    Windows)
        echo "Hello Bill Gates!!"
        ;;
    Linux)
        echo "Hello Linus!!"
        ;;
    OpenBSD)
        echo "Hello Theo!!"
        ;;
    Solaris)
        echo "Hello Bill Joy!!"
        ;;
    emacs)
        echo "Hello RMS!!"
        ;;
    sendmail)
        echo "Hello Eric!!"
        ;;
    *)
        echo "Hello World!!"
        ;;
esac

見て判ると思いますが、明らかに可読性が上がっていますよね。
実行結果は以下の通りです。

# ./helloworld5.sh Windows
Hello Bill Gates!!
# ./helloworld5.sh Linux
Hello Linus!!
# ./helloworld5.sh OpenBSD
Hello Theo!!
# ./helloworld5.sh Solaris
Hello Bill Joy!!
# ./helloworld5.sh emacs
Hello RMS!!
# ./helloworld5.sh sendmail
Hello Eric!!
# ./helloworld5.sh 
Hello World!!
#

「if」文と同じく、想定通りに実装されているようです。

では、処理速度はどうなのでしょうか?
6項目程度では、体感できる差は無いと言ってよいでしょう。
ですが、詳細な処理内容を比較すれば、差は歴然です。

前々回、バグを確認した時と同じ様に、詳細な処理ログを見てみましょう。
スクリプト1行目に「-x」を付与してから、第一オプションに何も指定せずに実行してみてください。

・if文の場合

# ./helloworld4.sh 
+ V_OPTION=
+ '[' '' = Windows ']'
+ '[' '' = Linux ']'
+ '[' '' = OpenBSD ']'
+ '[' '' = Solaris ']'
+ '[' '' = emacs ']'
+ '[' '' = sendmail ']'
+ echo 'Hello World!!'
Hello World!!
# 

オプションに何も指定していない場合は例外処理になりますので、if文の実装だと全ての条件文(if文、elif文)を処理しないと、「else」まで辿り着く事ができません。
今回のスクリプトだと、6回の条件文(if文、elif文)を通過して、やっと例外処理(else)に辿り着く事になります。

・case文の場合

# ./helloworld5.sh 
+ V_OPTION=
+ case "${V_OPTION}" in
+ echo 'Hello World!!'
Hello World!!
#

ところがcase文の場合、1回の判定で例外処理を行っています。
その為、項目が増えれば増える程、処理速度の差が開いていきます。
実際、case文でそこまで多くの項目を判定する事は無いと思いますが、この判定をループ処理で行う事は多々あります。
そうなった場合、ループ処理回数が増えれば増える程、処理速度は明確に違ってきます。
複数の選択肢がある様な条件判定の場合は、率先してcase文を使う様に心がけましょう。