DESIGN / CODE / INFRA / AI

解説

関数を抜けたいだけなのに処理が止まる?Bashのreturnとexitの範囲を知ろう

関数を終えるreturnと、シェルを終えるexitを子Bashで比較。呼び出し後の表示、終了コード、親へ戻る範囲を安全な例で確認。

関数を抜けたいだけなのに処理が止まる?Bashのreturnとexitの範囲を知ろう
保存リストを見る →
この記事の道しるべ 4項目

シェルの関数でエラーを返したいだけなのに、その後の処理まで止まってしまった。Bashのreturnとexitは、どちらも数字を返せるけれど、終える範囲が違うよ。

このページでは、関数を「名前を付けてまとめた処理」として使い、二つの違いを比べる。Bashのターミナルが必要だよ。exitで普段使うターミナルを閉じないよう、例は新しく起動する子Bashの中だけで実行する。

returnは関数から戻る

bash --noprofile --norc -c '
  task() {
    printf "return:inside\n"
    return 7
    printf "return:unreachable\n"
  }
  task
  printf "return:after status=%s\n" "$?"
'

表示はreturn:insideとreturn:after status=7の二行になる。関数の中のreturnより後ろは実行されないが、呼び出し元へは戻るので、その後のprintfは実行されるんだ。

7はこの例で決めた失敗を表す数字だよ。0以外の終了状態を、呼び出し元でどう扱うかは別に決める必要がある。ここでは、表示して内容を観察している。

exitは実行しているシェルを終える

bash --noprofile --norc -c '
  task() {
    printf "exit:inside\n"
    exit 7
  }
  task
  printf "exit:after\n"
'
printf 'parent:child-status=%s\n' "$?"

子の表示はexit:insideだけ。exitは関数を抜けて呼び出し元へ戻るのではなく、ここで動いている子Bash自体を終える。そのため、子のexit:afterは表示されないよ。

一方、子Bashを起動した親のターミナルには制御が戻り、parent:child-status=7が表示される。普段のターミナルで直接exit 7と入力する例とは違うんだ。

数字を表示する前に読む理由

$?には、直前に実行したコマンドの終了状態が入る。間に別のコマンドを挟むと、そのコマンドの結果へ変わるので、必要ならすぐ変数へ保存しよう。

task
status=$?
printf 'status=%s\n' "$status"

これは書き方を示す断片なので、先ほど定義した関数と同じシェル内で使う必要があるよ。子Bashの中だけに定義したtaskが、親にも自動で定義されるわけではない。

この例の前提と、実作業の確認

例の子Bashではset -eを有効にしていない。失敗時にシェルを止める設定や、&&・||などの条件との組み合わせでは、呼び出した後の流れにも別の規則が加わる。returnなら後続が必ず動く、という一般化はしないでね。

読み込んだスクリプトで使うreturnなど、関数以外の用法もあるけれど、ここでは扱わない。まずは「関数から戻るのか、そのシェルを終えるのか」を言葉にしてから選ぼう。

編集部では2026年10月29日にBashの関数・組み込みコマンドの説明と照合し、子と親の表示、7という終了状態、実行されない行を検証した。終了コード、&&と||へ進むと、受け取った結果を使う判断も整理できるよ。

参考・出典

公開と変更の記録
  • :初回公開

日時は日本時間です。記載のある変更は、確認できた編集記録に基づきます。更新日の違いだけから変更内容を推測して載せることはありません。

更新・訂正の方針 →

みんなのコメント 0

感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。

最初のコメントをどうぞ。