プロセスの終了ステータスを$?
で取得し、if文で判断するshellスクリプトは書いてはいけない。if文を使うということは、条件文としてtest
コマンドを使うことになり、ifとelif(の中のtest
コマンド)が実行されるたびに、終了ステータス$?
が変わってしまう。
以下にサンプルコードを書く。
$ cat test.sh #!/bin/sh return_val() { echo '"$?" is 2' return 2 } return_val if [ $? = 3 ]; then echo '"[ $? = 3 ]" is matched. $? is' $? elif [ $? = 2 ]; then echo '"[ $? = 2 ]" is matched. $? is' $? else echo '"else" is matched. $? is' $? fi
実行してみると、次のようにif文が正しくマッチしない。
$ sh test.sh "$?" is 2 "else" is matched. $? is 1
return_val
を実行した結果、$?
は2になる。しかし、初めのif [ $? = 3 ]
が条件にマッチしないため、test
コマンド([
コマンド)の終了ステータスである1が$?
を上書きしてしまう。その結果、つぎのelif [ $? = 2 ]
が本来マッチしてほしいはずなのにマッチしなくなってしまう。
運よくif文の初めの条件文でマッチした場合は正しく判定できるが、if文内のコマンド実行部分ではすでに$?
が0で上書きされてしまっているため、$?
を使用できない。
対策は簡単で、$?
を変数に格納してあげればいい。
$ cat test2.sh #!/bin/sh return_val() { echo '"$?" is 2' return 2 } return_val ret=$? if [ $ret = 3 ]; then echo '"[ $ret = 3 ]" is matched. $? is' $? elif [ $ret = 2 ]; then echo '"[ $ret = 2 ]" is matched. $? is' $? else echo '"else" is matched. $? is' $? fi
実行してみると、正しいif文にマッチする。ただし$?はif文内では先述の通りtest
コマンドの結果で上書きされているため使用できない。
$ sh test.sh "$?" is 2 "[ $ret = 2 ]" is matched. $? is 0
if test $? = 3
と書いていれば、$?
がtest
コマンドで上書きされることに気づきやすい。しかし、今回のように[
コマンドで書いてしまうと、実際にはtest
コマンドで書いたことと同じ扱いなのに、if文の構文のように見えてしまう。コマンドを実行したという意識が希薄になって$?
が上書きされることにも気づきにくくなるので、注意が必要。
Copyright © 2017 システム開発メモ All Rights Reserved.