シェルスクリプトの基礎 - 終了ステータス

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動

概要

コマンド終了時には、終了ステータスと呼ばれるコマンドの成否を表す数値が特殊変数$?に自動で設定される。
各コマンドにより異なるが、コマンド成功時は0、失敗時は1(コマンドやエラーの種類によっては0以外)が設定される。

直前に実行したコマンドの成否は、以下のように、特殊変数$?に設定されている値で確認できる。

<コマンド>
echo $?



終了ステータスの設定

シェルスクリプトでは、exitコマンドに指定したパラメータ(0または1~255の正の整数)が終了ステータスとなる。
正常に終了した時はexit 0、異常終了時はexit 1で終了することが慣例である。

関数も同様に、returnコマンドに指定したパラメータが終了ステータスとなる。

 exit <数値>
 return <数値>


exitコマンドやreturnコマンドに終了ステータスを指定することで、任意の終了ステータスでシェルスクリプトおよび関数を終了することができる。

exitコマンドは省略可能であるが、省略する場合は、シェルスクリプト内で最後に実行したコマンドの終了ステータスが、
シェルスクリプトの終了ステータスとなる。
また、returnコマンドも同様となる。

cat <存在しないファイル名>  # catコマンドが失敗する場合、終了ステータスは1となる
echo $?

# 出力
1


touch <新規ファイル名>  # catコマンドが成功する場合、終了ステータスが0となる
cat <新規ファイル名>
echo $?

# 出力
0


以下の例では、exitコマンドを使用して終了ステータスを設定している。

 #!/bin/bash
 
 # カレントディレクトリにあるfooディレクトリの存在を確認
 if [ -d ./foo ]; then
    echo "ディレクトリが存在する"
    exit 0
 else
    echo "ディレクトリが存在しない"
    exit 1
 fi
 
 # 出力
 # ファイルが存在する場合
 echo $?
 0
 
 # ディレクトリが存在しない場合
 echo $?
 1



終了ステータスを判定してコマンドを実行する

コマンドが成功した場合のみ次のコマンドを実行する

以下のように、&&でコマンドを繋ぐ場合、直前のコマンドが成功した場合のみ、次のコマンドが実行される。

コマンドが&&で結合される場合、シェルはcommand1の終了ステータスを判断して、0(成功)である場合のみcommand2を実行する。
さらに、複数のコマンドが結合される場合も、直前のコマンドが成功した場合のみ、次のコマンドが実行される。

command1 && command2
command1 && command2 && command3


直前のコマンドが失敗していると困る処理において、&&を使用する場合、直前のコマンドが成功していることが保証されるため、
連続して2つのコマンドを実行するよりも確実性の高い処理になる。

ls hogehoge && echo "exists."


コマンドが失敗した場合のみ次のコマンドを実行する

以下のように、||でコマンドを繋ぐ場合、直前のコマンドが失敗した場合のみ、次のコマンドが実行される。

コマンドが||で結合されると場合、シェルはcommand1の終了ステータスを判断して、0以外(失敗)であった場合のみcommand2を実行する。
さらに、複数のコマンドが結合される場合も、直前のコマンドが失敗した場合のみ、次のコマンドが実行される。

command1 || command2
command1 || command2 || command3


command1が失敗した場合のみcommand2を試すといった処理を行う場合、この||を使用する。

ls fugafuga 2>/dev/null || echo "not exists."


rm -fの終了ステータス

rm -fコマンド(強制削除)の終了ステータスは、常に0となるため、&&または||を使用してコマンドを連結しても期待する結果にはならない。
例えば、ファイルを削除できなかった場合のみ、あるコマンドを実行するといった処理は、終了ステータスの判定ではできない。

# -fオプションを付加して実行する場合、ファイルが存在しなくともrmコマンドは成功する
# ただし、rm -fを使用する場合でも、/tmp等のスティッキービットが設定されているディレクトリで、
# 他のユーザのファイルを削除しようとした場合には、終了ステータスが1となる。
rm -f foo
echo $?
0


このように、rm -fの終了ステータスは判断が難しいため、強制削除の終了ステータス判定処理は避けること。


終了ステータスの応用

入力値の判別(数値または文字列)

キーボードから入力する値が、数値または文字列かを判別する場合、exprコマンドとその終了ステータスを使用する。

exprコマンド(四則演算などに使用するコマンド)は、以下のような終了ステータスである。

  • 数値同士で演算を行った場合は、0
  • 数値同士でも計算結果が0になる場合は、1
  • 数値以外で演算を行った場合(計算できない場合)は、2以上


つまり、数値とキーボードからの入力値をexprコマンドで演算を行い、
終了ステータスが0または1ならば入力値は数値、2以上ならば入力値は数値以外の文字列と判断することができる。

以下の例では、exprコマンドと終了ステータスを使用して入力値の判定を行っている。

 #!/bin/sh
 
 # キーボードから入力(-n を付けると改行なしで出力する)
 read -p "数字を入力してください > " KEY
 
 # 入力値に1を加算して、その終了ステータスを判定する
 expr 1 + $KEY >/dev/null 2>&1
 case $? in
    0 | 1) echo "[\$?=$?] 数字が入力されました > $KEY" ;;
    *) echo "[\$?=$?] 数字以外が入力されました > $KEY" ;;
 esac
 
 exit 0


上記のシェルスクリプトの実行結果は、以下の通りとなる。

数字を入力してください >1
[$?=0] 数字が入力されました >1
#↑expr コマンドが成功するので、終了ステータスは「0」になる。

$ ./input_check.sh 数字を入力してください >-1 [$?=1] 数字が入力されました >-1

  1. ↑expr コマンドは成功するが計算結果が0となるので、終了ステータスは「1」になる。

$ ./input_check.sh 数字を入力してください >hoge [$?=3] 数字以外が入力されました >hoge

  1. ↑計算できないため expr コマンドが失敗し、終了ステータスは「3」になる。


パイプ処理の終了ステータスの取得

パイプ処理を行う場合、特殊変数$?に設定される値は、パイプ処理の最後に実行するコマンドの終了ステータスとなる。

以下のような例を考える。

exit 0 | exit 1 | exit 2
echo $?

# 出力
2


この時、最後のコマンドの終了ステータスではなく、パイプ処理の先頭または途中で実行するコマンドの終了ステータスを取得したい場合がある。
この場合、特殊変数$PIPESTATUS(配列)を参照することで、パイプ処理にて実行された各コマンドの終了ステータスを取得することができる。
(ただし、bashのみ)

以下の例では、全要素(全終了ステータス)を参照しているが、個別に参照することもできる。
詳細は、シェルスクリプトの基礎 - 配列を参照すること。

exit 0 | exit 1 | exit 2
echo ${PIPESTATUS[@]}  # $PIPESTATUS配列において、@を指定して全要素を出力している

# 出力
0 1 2