はじめに
誰かに仕事を頼むたびに、その人が作業後に小さなカードを渡してくれると想像してください。
- Green Card(0) は「完璧にできました」という意味です。
- Red Card(1-255) は「何か問題が起きました」という意味です。
Linux では、すべてのコマンドがまさにこの仕組みを使います。これは 終了コード(exit code) または 終了ステータス(exit status) と呼ばれます。
0:成功。0 以外:失敗。
通常、終了コードを確認しない限り表示されないため、多くの初心者は終了コードを意識しません。しかし、プロのスクリプト作成者は終了コードを注意深く確認します。終了ステータスは、コマンドやスクリプトの間で成功または失敗を伝える、シェルの主要な仕組みの 1 つだからです。
これは Unix の基本的な設計パターンです。小さなプログラム同士が、単純な規則を使って通信します。テキストは標準出力を通じて渡され、成功または失敗は終了ステータスを通じて伝わります。自動化で終了コードが重要になるのはこのためです。
この実験では、変数 $? を使ってこの秘密のカードを読み取り、スクリプトが問題を広げる前に停止させる方法を学びます。
$? でコマンドの状態を確認する
この見えない「成績表」は、どうすれば確認できるのでしょうか。コマンドを実行した直後に、特別な変数 $? を確認します。
まず、成功する場合を見てみましょう。
ターミナルを開き、ファイルを一覧表示します。
ls -l
正常に実行できましたね。それでは「成績はどうだったのか」を確認します。
echo $?
0 と表示されるはずです。Linux では、0 はエラーの数が 0 であることを表します。エラーが 0 件なら、完全な成功です。
次に、意図的に失敗させてみましょう。存在しないファイルを一覧表示します。
ls ghosts.txt
Linux はファイルを見つけられないというエラーを表示します。もう一度、成績を確認します。
echo $?
2 と表示されます。0 ではないため、失敗です。
0 以外の具体的な数値は、コマンドや状況によって異なります。ただし、現時点で覚える重要なルールは単純です。0 は成功、それ以外は失敗を意味します。今後、スクリプトやツールを扱うと、問題の種類ごとに異なる 0 以外のコードを使うプログラムもあります。
重要なルール:$? は短期記憶です。直前に実行した 最後の処理だけ を記憶します。ls を実行して失敗した後、echo "hello" を実行し、その後で $? を確認すると、echo が成功したため 0 と表示されます。終了ステータスは必ず直後に確認してください。
失敗するスクリプトを作成する
ここが危険なポイントです。デフォルトでは、スクリプトは Red Card を無視し、そのまま処理を続けます。
次のようなスクリプトを考えてみましょう。
- バックアップを作成する。
- 元のファイルを削除する。
手順 1 が失敗した場合(バックアップ先の容量がいっぱいになった場合など)でも、手順 2 は実行されるため、すべてを失う可能性があります。
この「目隠しスクリプト」を再現してみましょう。
copy_script.sh を作成します。
nano copy_script.sh
次のコードを入力します。このコードは存在しないファイルをコピーしようとした後、成功したようなメッセージを表示します。
#!/bin/bash
## Attempt to copy a ghost
cp ghost_file.txt destination.txt
## Print a happy message
echo "Script finished execution"
保存して終了します。実行権限を付与します。
chmod +x copy_script.sh
実行します。
./copy_script.sh
cp のエラーメッセージが表示された後に、Script finished execution も表示されます。スクリプトは失敗したのに、そのまま処理を続けています。
スクリプト自体の成績を確認します。
echo $?
0 が返ります。なぜでしょうか。最後に実行したコマンド(echo)が成功したためです。このスクリプトは、実際には主要な処理に失敗したのに、システムには成功したと報告しています。
ここで重要な点を学びました。シェルスクリプトにも終了ステータスがあります。特別な処理を何もしなければ、多くの場合、最後に実行したコマンドの終了ステータスがスクリプトの終了ステータスになります。そのため、スクリプトは途中で発生した失敗を誤って隠してしまうことがあります。
エラー処理を実装する
スクリプトが Red Light で停止するように設定する必要があります。
一般的には、次の構造を使います。「コマンド X を実行する。終了コードが 0 でなければ、異常終了する。」
[ $? -ne 0 ] という構文は、test コマンドを角括弧の形式で記述したものです。-ne は数値の「等しくない」を意味します。つまり、シェルは「直前のコマンドの終了ステータスは 0 ではないか」と確認しています。
copy_script.sh を編集します。
nano copy_script.sh
次の内容に変更します。
#!/bin/bash
cp ghost_file.txt destination.txt
## Check if the score is NOT equal (-ne) to 0
if [ $? -ne 0 ]; then
echo "Error: The copy operation failed!"
exit 1
fi
echo "Script finished execution"
読み解き方:
if [ $? -ne 0 ]:ガードがカードを確認します。exit 1:Red Button です。スクリプトを直ちに終了し、グローバルな成績を 1(エラー)に設定します。
exit コマンドは、現在のシェルスクリプトを終了し、起動元にその数値を返します。起動元には、ターミナル、別のスクリプト、自動化システムなどがあります。つまり、単にスクリプトの処理をローカルで停止するだけでなく、外部にステータスを報告しています。
保存して終了します。
もう一度実行します。
./copy_script.sh
スクリプトはエラーを表示して終了します。Script finished execution は表示されません。
成績を確認します。
echo $?
1 と表示されます。これで、スクリプトは失敗を正しく報告しています。
まとめ
コマンドが使う秘密の言語を読み取る方法を学びました。
この実験では、次の内容を実践しました。
- 成績表を読む:
$?を使い、コマンドが成功したか(0)、失敗したか(1-255)を確認する。 - 嘘を見抜く:注意しなければ、スクリプトがエラーを隠してしまうことを確認する。
- 審判になる:問題が発生したときに
exit 1を使ってゲームを止める。
この「確認して終了する」パターンが、脆弱なスクリプトとプロフェッショナルなスクリプトの違いです。



