Bashスクリプトを作成する際、多くの開発者が直面する問題の一つに「途中でコマンドが失敗したにもかかわらず、処理が最後まで進行してしまう」という現象があります。
2026年のシステム運用においても、自動化スクリプトの信頼性は非常に重要視されており、微細なエラーを見逃すことが深刻なデータ破損やデプロイ不備を招くリスクは変わりません。
このような事故を未然に防ぎ、エラーが発生した瞬間にスクリプトを安全に停止させるための標準的な手法がset -eオプションです。
この記事では、set -eの基本的な使い方から、実務で陥りやすい「エラーを検知できない例外パターン」の解決策までを詳しく解説します。
set -e(errexit)の役割と動作原理
Bashにおけるset -eは、スクリプト内で実行されたコマンドが0以外の終了ステータスを返した際に、即座に実行を中断して終了させるための設定です。
デフォルトの状態では、Bashは実行したコマンドがエラーになっても、次の行に記述された処理を継続しようと試みます。
例えば、ディレクトリの作成(mkdir)に失敗した直後のファイル書き込み処理が実行されると、意図しない場所にファイルが作成されるなどの問題が発生します。
set -eを用いることで、こうした「連鎖的なエラーによる被害の拡大」を最小限に抑えることが可能になります。
基本設定の方法
set -eを有効にするには、主に2つの方法があります。
一つはスクリプトの冒頭(シバン行の直後など)に記述する方法、もう一つはシバン(#!)の中に直接含める方法です。
#!/bin/bash
# スクリプトの途中でエラーが発生したら停止させる
set -e
# このコマンドが失敗すると、次のechoは実行されません
ls /non_existent_directory
echo "このメッセージは表示されません"
ls: /non_existent_directory: No such file or directory
(スクリプトがここで終了する)
一般的には、可読性とメンテナンス性の観点からスクリプトの先頭付近に明示的に記述する方式が広く推奨されています。
set -e だけで防げない「意図しない続行」の解決策
set -eは非常に有用ですが、万能ではないという点に注意が必要です。
実は、特定の条件下ではコマンドが失敗してもスクリプトが停止せず、エラーを見逃してしまうケースが存在します。
現代のシェルスクリプト開発では、これらの弱点を補うために追加の設定を組み合わせることが標準的となっています。
パイプラインでのエラー検知 (set -o pipefail)
多くの初心者が陥る罠が、パイプ(|)を使用したコマンドラインでの挙動です。
set -eだけでは、パイプの最後にあるコマンドが成功した場合、その途中でエラーが発生していても無視されてしまいます。
#!/bin/bash
set -e
# 存在しないファイルをcatしようとしても、grepが成功判定(終了コード0)を返すと停止しません
cat non_existent_file | grep "target"
echo "エラーを無視して継続してしまいました"
cat: non_existent_file: No such file or directory
エラーを無視して継続してしまいました
この問題を解決するには、set -o pipefailを追加で設定する必要があります。
これにより、パイプラインの途中で一つでも失敗したコマンドがあれば、そのパイプライン全体の終了ステータスがエラーとなり、set -eによってスクリプトが停止します。
論理演算子 (&&, ||) を使用した連結
&&(AND)や||(OR)を使ってコマンドを連結した場合も、set -eの対象外となることがあります。
Bashの仕様上、条件判定の一部として扱われるコマンドのエラーは「意図的なチェック」とみなされ、スクリプトの停止が阻害されます。
#!/bin/bash
set -e
# && で連結されたコマンドが失敗しても、評価の一部とみなされて停止しない場合があります
false && echo "Not shown"
echo "まだ実行が続いています"
複雑な論理分岐の中でエラーを確実に捕捉したい場合は、安易に && を使わずに、if文を用いて明示的にエラー判定を行うか、必要なタイミングで再度set -eの挙動を確認する必要があります。
堅牢なスクリプトのための「三種の神器」設定
2026年現在のベストプラクティスとして、Bashスクリプトを記述する際は以下の3つのオプションをセットで設定することが強く推奨されています。
| オプション | 効果 | 重要性 |
|---|---|---|
set -e | エラー(非ゼロ終了ステータス)発生時に即時停止する。 | 必須級 |
set -u | 未定義の変数を使用しようとしたときにエラーとして停止する。 | 非常に高い |
set -o pipefail | パイプライン内のどこかでエラーがあれば失敗とみなす。 | 必須級 |
これらを一つにまとめて記述する際は、以下のような短縮記法が頻繁に使用されます。
# 長年愛用される最も安全な基本セット
set -euo pipefail
特にset -uを組み合わせることで、タイポによる変数の参照ミスなども防げるため、保守性が飛躍的に向上します。
エラー後に「後始末」を行いたい場合 (trapコマンド)
set -eを有効にするとエラー時に即停止するため、一時ファイルの削除や接続のクローズといった「終了処理」が行われなくなることを心配されるかもしれません。
そのような場合は、trapコマンドを併用することで、スクリプトが異常終了した際に特定の関数を実行させることが可能です。
#!/bin/bash
set -euo pipefail
# スクリプト終了時に必ず実行される後始末関数
cleanup() {
echo "一時ファイルを削除しています..."
rm -f /tmp/work_file
}
# EXITシグナル(終了時)にcleanup関数を呼び出すよう登録
trap cleanup EXIT
# ここでエラーが発生しても cleanup が呼ばれます
touch /tmp/work_file
ls /non_existent_file
set -eで「止める」ことと、trapで「片付ける」ことをセットで覚えることが、プロフェッショナルなスクリプト作成の第一歩となります。
意図的にエラーを無視して続行させたいケースの対処法
稀に、特定のコマンドが失敗しても無視して後続処理を続けたいケースがあります。
例えば、ディレクトリを削除しようとして「元々存在しなかった」ためにエラーが返る場合などが該当します。
このようなケースで全体のset -e設定を維持しつつ、局所的にエラーを許容するには、論理和演算子(OR)を使用して「空(true)」の結果を返すと良いでしょう。
# エラーを許容したいコマンドの末尾に || true を付ける
rm /tmp/possible_missing_file || true
# あるいは特定の範囲だけ一時的に set -e を解除する
set +e
command_to_tolerate_error
set -e
基本的には|| trueを用いる方が、設定の解除忘れを防げるため安全です。
まとめ
Bashスクリプトにおいて、set -eは予期せぬエラーからシステムを守るための最も基本的かつ強力な武器です。
しかし、パイプラインの挙動や論理演算子との干渉など、特有の癖を理解していなければ、「止まったはずなのに止まっていない」という深刻なミスを見逃すことになりかねません。
そのため、現代的なスクリプトではset -euo pipefailをデフォルト設定とし、エラー時の動作を一貫させることが強く求められます。
さらに、trapコマンドによる終了処理の自動化を組み合わせることで、エラー発生時に「安全に、かつ綺麗に」停止するスクリプトを構築することが可能です。
まずは今日から作成するすべてのスクリプトにset -euo pipefailを記述し、不慮の事故を防ぐ習慣を身につけていきましょう。
