Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

応用編:エラー処理・デバッグと安全な実行

Bash スクリプトの暴走やサイレント障害を防ぐ厳格モード(set -euo pipefail)、trap によるエラー捕捉、および効果的なデバッグ手法を解説します。

デフォルトの Bash が抱える危険性

Bash は既定の状態では以下のような挙動を示します。

  • コマンドの実行が途中で失敗しても、何事もなかったかのように後続の処理を継続する
  • 誤字などによる未定義の変数を参照しても、単に空文字として処理を続行する
  • パイプラインの途中のコマンドがエラーになっても、最後のコマンドが成功すれば成功とみなす

例えば rm -rf "$TARGET_DIR/*" において $TARGET_DIR が未定義の場合、空文字とみなされてルートディレクトリ直下(/*)が削除されるといった重大な事故に直結します。

厳格モード(set -euo pipefail)

スクリプトの冒頭で以下のオプションを設定することで、安全な実行環境(厳格モード)を確立します。

#!/usr/bin/env bash
set -euo pipefail
オプション略称役割
set -eerrexitコマンドが1つでも失敗(終了ステータスが非0)したら即座にスクリプトを中断する
set -unounset未定義の変数を参照した場合、エラーを表示してスクリプトを中断する
set -o pipefail-パイプライン内で1つでも失敗したコマンドがあれば、パイプライン全体を失敗とする

» 意図したエラーの許容

set -e 環境下で、特定コマンドのエラー(終了コード非0)を無視して継続したい場合は、|| true を付与するか if 文で判定します。

# grep で検索結果が0件(終了コード 1)でもスクリプトを停止させない
grep "optional_setting" config.txt || true

trap によるエラーの補足と通知

trap コマンドを使用すると、スクリプト内でエラー(ERR)が発生した瞬間に指定した関数を自動実行できます。組み込み変数 $LINENO(行番号)や $BASH_COMMAND(失敗したコマンド)を記録するのに役立ちます。

#!/usr/bin/env bash
set -euo pipefail

# エラー発生時のハンドラ関数
on_error() {
    local exit_code="$1"
    local line_no="$2"
    local command="$3"
    echo "[ERROR] Command '$command' failed at line $line_no with exit code $exit_code" >&2
}

# ERR シグナルをフック
trap 'on_error $? $LINENO "$BASH_COMMAND"' ERR

# 意図的にエラーを起こすテスト
ls /path/to/missing_file

デバッグ手法とトレース実行

スクリプトの不具合を素早く調査・特定するための技法です。

» 構文チェック(-n オプション)

スクリプトを実行せずに、文法エラー(if の閉じ忘れ等)の有無だけを静的に検証します。

bash -n script.sh

» 実行トレース(set -x)

実行される各コマンドとその引数を、実際に評価された値に展開した状態でターミナルに出力します。

# スクリプト全体をデバッグ実行
bash -x script.sh

# スクリプト内の特定ブロックだけトレースを有効化
set -x
critical_processing
set +x  # トレースを停止

» 構造化されたログ出力

標準出力と標準エラー出力を適切に分離し、タイムスタンプを付与したログ関数を用意します。

log_info()  { echo "[INFO]  $(date +%T) - $*"; }
log_warn()  { echo "[WARN]  $(date +%T) - $*" >&2; }
log_error() { echo "[ERROR] $(date +%T) - $*" >&2; }