終了ステータス(exit status)入門 - $? と && || で処理を分岐する
この記事で学べること
- コマンドの 成功・失敗を表す「終了ステータス(exit status)」 という考え方が分かる
$?で 直前のコマンドの結果 を確認できるようになる&&と||で 「成功したら」「失敗したら」 の処理を分岐できる- シェルスクリプトで エラーをきちんと扱う第一歩 が踏み出せる
結論(先に覚えるべき型)
- コマンドの結果は 数字 で返ってくる(
0が成功、それ以外が失敗) - 直前の結果を見たい →
echo $? - 成功したら次へ →
&& - 失敗したら次へ →
||
1. 終了ステータスとは何か?
結論: 終了ステータスはコマンドが返す数字。
0が成功、1〜255が失敗を表す。
0 なら成功、0 以外なら失敗。この1個の数字でコマンドの成否を判断できる。終了ステータスの基本ルール
| 数字 | 意味 |
|---|---|
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 の終了ステータス(成功なので 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"
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 になる
確認したいコマンドの すぐ次 で $? を見ること。間に別のコマンドを挟むと $? が上書きされる。
7-3. 終了ステータスを自分で返したい
スクリプトやコマンドの中で、成功・失敗を呼び出し元に伝えたいときは exit を使う。
exit 0 # 成功として終了 exit 1 # 失敗として終了
exit に数字を付けないと 直前のコマンドの終了ステータス がそのまま返る。
8. ミニ課題:実際にやってみよう
結論: 確認・成功時実行・失敗時実行の3問で、$? と && || を手で確かめる。
課題1: 適当なコマンド(例: ls)を実行した直後に、その終了ステータスを画面に表示しよう。
ヒントを見る
直前の結果は $? に入っている。
解答例
$ ls $ echo $?
課題2: mkdir test-dir が成功したときだけ「作成成功」と表示しよう。
ヒントを見る
「成功したら次」は &&。
解答例
$ mkdir test-dir && echo "作成成功"
課題3: 存在しないディレクトリへ cd を試み、失敗したら「移動できません」と表示しよう。
ヒントを見る
「失敗したら次」は ||。
解答例
$ cd /not-exist || echo "移動できません"
9. コピペ用テンプレート
結論: 確認・成功時・失敗時・成否分岐・終了のよく使う型をまとめて手元に置いておく。
よく使う型をまとめておく
# 直前の終了ステータスを確認 echo $? # 成功したときだけ次を実行 コマンドA && コマンドB # 失敗したときだけ次を実行 コマンドA || コマンドB # 成功なら A、失敗なら B(簡易分岐) コマンド && echo "OK" || echo "NG" # 失敗したらスクリプトを終了 コマンド || exit 1 # スクリプトを明示的に成功 / 失敗で終わらせる exit 0 # 成功 exit 1 # 失敗