終了ステータス(exit status)入門 - $? と && || で処理を分岐する
この記事でできるようになること
- `echo $?` で直前のコマンドが成功したか失敗したかを確認できる
- `&&` と `||` で「成功したら」「失敗したら」の処理を書き分けられる
- `exit 0` と `exit 1` で、成功と失敗を呼び出し元に伝えられる
前提知識(先に読むと理解しやすい記事)
この記事で学べること
- コマンドの 成功・失敗を表す「終了ステータス(exit status)」 という考え方がわかります
$?で 直前のコマンドの結果 を確認できるようになります&&と||で 「成功したら」「失敗したら」 の処理を書き分けられます- シェルスクリプトで エラーをきちんと扱う第一歩 を踏み出せます
言葉の整理
- 終了ステータス:コマンドが終わるときに返す数字です。「終了コード」「exit status」「exit code」は、すべて同じものを指す別の呼び方です。
- 変数:値を入れておく名前付きの箱です。
$?はシェル入力したコマンドを解釈してコンピュータに実行させる対話プログラム。が自動で用意する特別な変数です。 - シェルスクリプト:コマンドを順番に並べて書いたファイルです。
この記事で使う ls / echo / cd / mkdir は、既存のファイルを消しません。安心して試してください。
結論(先に覚えるべき型)
- コマンドの結果は 数字 で返ってくる(
0が成功、それ以外が失敗) - 直前の結果を見たい →
echo $? - 成功したら次へ →
&& - 失敗したら次へ →
||
1. 終了ステータスとは何か?
結論: 終了ステータスはコマンドが返す数字。
0が成功、1〜255が失敗を表す。
0 なら成功、0 以外なら失敗 だよ。終了ステータスの基本ルール
| 数字 | 意味 |
|---|---|
0 |
成功 |
1〜255 |
失敗(何らかの異常) |
「0 が成功」という点は直感に反します。最初に必ず覚えてください。
2. なぜ終了ステータスが重要なのか?
結論: 終了ステータスがあるから「成功したときだけ次を実行」といった自動化ができる。
画面の表示だけで成否を判断するのは危険です。「Error」という文字が出ていなくても、失敗していることがあります。
機械が判断できる終了ステータスを使う ほうが確実です。
3. $? で結果を確認する
結論:
$?には直前のコマンドの終了ステータスが入る。echo $?で中身を見られる。
直前に実行したコマンドの終了ステータスは、$? という特別な変数に入っています。この変数はシェルが自動で用意します。
$ ls /etc
hosts passwd ...
$ echo $?
0
0 が出ました! ls が成功したってことですね。ls してみて。$ ls /not-exist
ls: '/not-exist' にアクセスできません: そのようなファイルやディレクトリはありません
$ echo $?
2
2 が出ました。さきほどと数字が違います。0 以外が返る。ls は「ファイルが見つからない」というエラーのとき 2 を返す決まりなんだ。0 か、それ以外か だけ見れば十分だよ。$? は 直前のコマンドの結果 しか覚えていません。
echo $? を 2 回続けると、2 回目は 1 回目の echo の結果を表示します。echo は成功するので 0 になります。確認は 1 回だけにしてください。
4. && で「成功したら次」をつなぐ
結論:
cmd1 && cmd2は cmd1 が成功(0)したときだけ cmd2 を実行する。
&& は 「左が成功したら、右も実行する」 という意味の記号です。
$ mkdir backup && cp data.txt backup/
mkdir が成功したら cp する、という意味ですか。mkdir backup が失敗したら、cp は 実行されない。権限がなくて作れなかった場合などだね。# ビルドが成功したときだけテストを走らせる $ make && make test # ディレクトリへ移動できたときだけ中身を表示 $ cd /var/log && ls
&& は「前提が満たされたら進む」というイメージです。失敗したらそこで止まってほしい 処理を、安全につなげられます。
5. || で「失敗したら次」をつなぐ
結論:
cmd1 || cmd2は cmd1 が失敗したときだけ cmd2 を実行する。エラー時の対処に使う。
|| は && の逆です。「左が失敗したら、右を実行する」 という意味になります。
$ cd /var/log || echo "ディレクトリへ移動できませんでした"
cd が失敗したらメッセージを出す、ということですね。成功したときは出ないのでしょうか。cd が成功すれば echo は実行されない。|| は うまくいかなかったときの保険 として使うことが多いよ。# コマンドが無ければインストールを促す $ which jq || echo "jq が入っていません。インストールしてください" # 処理に失敗したらスクリプトを終了 $ cp data.txt backup/ || exit 1
&& と || の対比
cmd1 && cmd2 → cmd1 が【成功】したら cmd2 cmd1 || cmd2 → cmd1 が【失敗】したら cmd2
「&& は成功で進む」「|| は失敗で進む」と覚えてください。
6. && と || を組み合わせる
結論:
cmd && 成功時 || 失敗時で「成功なら A、失敗なら B」を1行で書ける。
&& と || をつなげると、成功したときと失敗したときで別々の処理を書けます。
$ ping -c1 example.com > /dev/null && echo "接続OK" || echo "接続NG"
echo "接続OK" 自体が失敗すると、失敗時の処理まで動いてしまう。if 文を使うほうが安全だよ。A && B || C は、if A then B else C と完全に同じではありません。B が失敗すると C も実行されます。
確実に分岐したいときは if 文を使ってください。書き方は「次に読む」の記事で説明します。
7. よくある初心者のつまずき
結論:
0が成功という点と、$?が直前専用という点を取り違えやすい。
7-1. 「0 が成功」を逆に覚える
0 が成功というのは今でも違和感があります。7-2. リナの失敗:$? を取るタイミングが遅い
$ ls /not-exist $ pwd # ← 余計なコマンドを挟んでしまった $ echo $? # これは pwd(成功)の結果 0 になる
0 が出ました。終了ステータスが壊れているのでしょうか。$? は直前の 1 つ分しか覚えていないんだ。pwd の結果に置きかわっていたんですか。pwd は成功するから 0 になる。ls の結果はもう上書きされているんだ。確認したいコマンドの すぐ次 で $? を見てください。間に別のコマンドを挟むと、$? の中身が上書きされます。
7-3. 終了ステータスを自分で返したい
スクリプトの中で、成功したか失敗したかを呼び出し元に伝えたいときは exit を使います。
exit 0 # 成功として終了 exit 1 # 失敗として終了
exit に数字を付けない場合は、直前のコマンドの終了ステータス がそのまま返ります。
8. ミニ課題:実際にやってみよう
結論: 確認・成功時実行・失敗時実行の3問で、$? と && || を手で確かめる。
課題 1: コマンドを 1 つ実行した直後に、その終了ステータスを画面に表示しよう。
ヒント 1(方向づけ)を見る
直前の結果は、シェルが自動で用意する特別な変数に入っています。その中身を画面に出します。
ヒント 2(コマンド名)を見る
変数は $? です。表示は echo です。
答えを見る
$ ls $ echo $?
0
ls が成功したので 0 が出ます。存在しないディレクトリファイルをまとめて整理する入れ物。Windows や macOS の「フォルダ」と同じもの。を指定すると、0 以外の数字に変わります。
課題 2: mkdir test-dir が成功したときだけ「作成成功」と表示しよう。
ヒント 1(方向づけ)を見る
2 つのコマンドを 1 行につなぎます。左が成功したときだけ右が動く記号を使います。
ヒント 2(コマンド名)を見る
使う記号は && です。表示は echo です。
答えを見る
$ mkdir test-dir && echo "作成成功"
作成成功
2 回目に同じコマンドを実行すると、mkdir が失敗します。そのため「作成成功」は表示されません。試したディレクトリは rmdir test-dir で削除できます。
課題 3: 存在しないディレクトリへ cd を試み、失敗したときだけ「移動できません」と表示しよう。
ヒント 1(方向づけ)を見る
課題 2 と同じ形ですが、記号が逆になります。左が失敗したときだけ右が動く記号を使います。
ヒント 2(コマンド名)を見る
使う記号は || です。移動は cd です。
答えを見る
$ cd /not-exist || echo "移動できません"
bash: cd: /not-exist: そのようなファイルやディレクトリはありません 移動できません
cd 自体のエラーメッセージと、自分で出したメッセージの両方が表示されます。
9. 振り返り
0 が成功で、それ以外が失敗ですね。echo $? で見られる。ただし直前の 1 回分だけだよ。&& は成功したら次へ、|| は失敗したら次へ。ここまで覚えました。exit 0 と exit 1 を書き分けてみて。10. コピペ用テンプレート
結論: 確認・成功時・失敗時・成否分岐・終了のよく使う型をまとめて手元に置いておく。
よく使う型をまとめておく
# 直前の終了ステータスを確認 echo $? # 成功したときだけ次を実行 コマンドA && コマンドB # 失敗したときだけ次を実行 コマンドA || コマンドB # 成功なら A、失敗なら B(簡易分岐) コマンド && echo "OK" || echo "NG" # 失敗したらスクリプトを終了 コマンド || exit 1 # スクリプトを明示的に成功 / 失敗で終わらせる exit 0 # 成功 exit 1 # 失敗
今日の 3 行まとめ
- コマンドは終わるときに数字を返す。
0が成功、それ以外は失敗 - 直前の結果は
echo $?で確認する。間に別のコマンドを挟まない &&は成功したら次へ進む。||は失敗したら次へ進む