戻り値について

前回の説明でtestコマンドと戻り値について解説しました。
今回はこの戻り値(終了コード)についてもう少し詳しく説明します。

前回の説明で少し触れましたが、この「戻り値」はどのようなコマンドでも処理が成功すれば「必ず」0を返します。これはbashスクリプトにとって、とても重要な事です。
スクリプトではこの戻り値によって処理の成功・失敗を判断する事が多いからです。
以下の様な要件のスクリプトを例として考えてみましょう。

・「test.txt」ファイルの中に「bbb」という文字列が存在するか判定する。

この様な要件の場合、スクリプトではなくコマンドラインから行う時は以下のコマンドで判別する事が多いですね。

# cat test.txt | grep "bbb"

もし、「test.txt」ファイルの中に「bbb」という文字列が存在すれば、その文字列を含む行が画面に表示されます。
人間がコマンドを入力し、画面の出力によって判断する事になります。

では、これをスクリプトで実装する場合、どうすれば良いでしょうか?

test文ではこういう条件を判定するオプションはなさそうです。
そんな時は判定できるコマンドを実行し、その「戻り値」で判定する事になります。

以下のスクリプトを「judge01.sh」と言う名前で作成してみましょう。

#!/bin/bash

cat test.txt | grep "bbb"
if [ $? -eq 0 ]
then
    echo "文字列bbbは存在する"
else
    echo "文字列bbbは存在しない"
fi

これは3行目の

cat test.txt | grep "bbb"

というコマンドが正常終了したかどうかを戻り値で比較しているスクリプトです。
「bbb」という文字列が存在すれば「文字列bbbは存在する」と画面に表示し、存在しなければ「文字列bbbは存在しない」と表示します。

それでは判定対象となる「test.txt」も作成してみましょう。
以下の様に記述してください。

aaa
bbb
ccc

さて、スクリプトを実行してみましょう。

# cat test.txt 
aaa
bbb
ccc
# ./judge01.sh 
bbb
文字列bbbは存在する
#

では、「test.txt」から文字列「bbb」が記述してある行を削除して、再度実行してみましょう。

# cat test.txt 
aaa
ccc
# ./judge01.sh 
文字列bbbは存在しない
# 

これで要件を満たしたスクリプトが完成しました。
ただ、本当に戻り値を判定しているのでしょうか?
実際に確認してみましょう。

文字列があるかどうかを判定する上記スクリプト3行目のコマンドをプロンプトから実行してください。
そして、コマンド実行後、プロンプトが帰ってきたら以下の様に「echo」コマンドで戻り値を表示してみましょう。

echo $?

「test.txt」ファイルに文字列「bbb」が存在する場合、しない場合、両方とも試してみてください。
存在する時は正常終了を表す戻り値「0」が表示され、存在しない場合は「1」が表示されるはずです。

これは「grep」コマンドが返している戻り値です。
「grep」コマンドは指定したパターンにマッチするかどうかを調べるコマンドですので、「bbb」という文字列のパターンにマッチした場合、コマンドとして想定した動作(正常終了)と判断し「0」を返し、マッチしなかった場合、コマンドとして想定していない動作と判断し「0以外」を返します。

このようにbashスクリプトではいろいろなコマンドの「戻り値」を使用して状況を判断し、その状況にあった処理を記述する事によって、人間の「目」を介して判断する事無く、さまざまな処理を自動化する事が可能となります。

そして、この作成したbashスクリプトも例外ではありません。
大規模なスクリプトになると、スクリプトから別のスクリプトを実行することも多々あります。
そんな時、戻り値はどのように取得するのでしょうか?
先ほど作成した「judge01.sh」はどのような戻り値を返しているのでしょうか?
スクリプト実行後に戻り値を表示してみましょう。
「test.txt」に文字列「bbb」が存在する場合、しない場合でどのようになるか見てみましょう。

・文字列「bbb」が存在する場合

# ./judge01.sh 
bbb
文字列bbbは存在する
# echo $?
0
# 

・文字列「bbb」が存在しない場合

# ./judge01.sh 
文字列bbbは存在しない
# echo $?
0
# 

どちらの場合も戻り値として「0」が返っていますね。
人間が実行し、目で判断できる場合は画面に存在するかしないかが表示されるので、これで良いのかもしれませんが、もし別のスクリプトからこのスクリプトを実行した場合、正常に終了したのかどうか判別できません。困ってしまいますね。

ですので、スクリプトも戻り値を指定して返さなければなりません。
bashスクリプトでは、戻り値に何も指定が無い場合、上記のようにスクリプトが終了する時に「0」を返します。
戻り値を指定して返しスクリプトを終了する時は、以下の様に「exit」コマンドを利用して戻り値を返します。

exit 戻り値

正常終了であれば、「exit 0」で、それ以外の時は任意の値を返せます。

では、この「exit」コマンドを追加して「judge01.sh」を以下の様に書き直してみましょう

#!/bin/bash

cat test.txt | grep "bbb"
if [ $? -eq 0 ]
then
    echo "文字列bbbは存在する"
else
    echo "文字列bbbは存在しない"
    exit 1
fi
exit 0

9行目と11行目に「exit」コマンドを追加しました。
この「exit」コマンドは、コマンド名から想像できる様に実行された時点でスクリプトが終了します。
終了する際に、指定された戻り値を出力する訳です。
「judge01.sh」は一番最後の行まで処理を実行すれば正常終了とみなし、異常の場合(文字列が存在しない場合)は「exit」コマンドを即時実行し「1」を返す事にします。
再度実行してみましょう。

・文字列「bbb」が存在する場合

# ./judge01.sh 
bbb
文字列bbbは存在する
# echo $?
0
# 

・文字列「bbb」が存在しない場合

# ./judge01.sh 
文字列bbbは存在しない
# echo $?
1
# 

どうでしょう。

・「test.txt」ファイルの中に「bbb」という文字列が存在するか判定する。

というスクリプトの要件にあわせて、存在する場合は戻り値を「0」で返し、存在しない場合は戻り値を「1」で返すという形にしました。
これで他のスクリプトからこのスクリプトを実行した際も、戻り値によって想定した動作をしているかどうかを判断できます。