堅牢なシェルスクリプト設計:エラー検知と自動クリーンアップによる安全な自動化の実装

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

堅牢なシェルスクリプト設計:エラー検知と自動クリーンアップによる安全な自動化の実装

【導入と前提】 API経由でデータを取得・加工するバッチ処理を、想定外の停止やリソース残留を防ぎつつ堅牢に自動化する手法を解説します。

  • 実行環境: GNU/Linux (Ubuntu 22.04+, RHEL 8+ 推奨)

  • 必須ツール: bash (4.4+), curl, jq, systemd

【処理フローと設計】

graph TD
Start["スクリプト開始"] --> Init["setオプション / trap設定"]
Init --> Temp["一時ファイル生成"]
Temp --> Fetch["curl: 外部APIデータ取得"]
Fetch --> Validate{"jq: バリデーション"}
Validate -- Success --> Process["データ加工・保存"]
Validate -- Failure --> ErrorHandler["エラーログ出力"]
Process --> Cleanup["trap: 一時ファイル削除"]
ErrorHandler --> Cleanup
Cleanup --> End["スクリプト終了"]

処理の各フェーズでエラーが発生しても、trap によって必ず一時リソースの解放が行われる設計です。

【実装:堅牢な自動化スクリプト】 以下は、外部APIからJSONデータを取得し、特定のフィールドを抽出して保存する実戦的なスクリプト例です。

#!/usr/bin/env bash

# --- 堅牢化設定 ---


# -e: エラー発生時に即座に終了


# -u: 未定義変数の参照時にエラー


# -o pipefail: パイプライン途中のエラーを無視しない

set -euo pipefail

# --- グローバル設定 ---

readonly API_URL="https://api.example.com/v1/metrics"
readonly WORK_DIR="/tmp/batch_job"
readonly OUT_FILE="/var/log/metrics_summary.json"
TMP_FILE=$(mktemp "/tmp/api_response.XXXXXX.json")

# --- クリーンアップ処理 ---


# スクリプト終了時(正常・異常問わず)に実行

cleanup() {
    local exit_code=$?
    echo "Cleaning up temporary files..."
    rm -f "$TMP_FILE"
    exit "$exit_code"
}

# EXITシグナルを捕捉してcleanup関数を実行

trap cleanup EXIT

# --- メイン処理 ---

echo "Starting data fetch..."

# curl のオプション解説:


# -s: 進捗を表示しない (silent)


# -S: エラー時はメッセージを表示


# -L: リダイレクトを追跡


# --retry: 失敗時に3回リトライ

curl -sSL --retry 3 --retry-delay 2 "$API_URL" -o "$TMP_FILE"

# jq によるバリデーションと加工


# -e: 結果がnullまたはfalseの場合に終了ステータス1を返す

if ! jq -e '.data' "$TMP_FILE" > /dev/null; then
    echo "Error: Invalid JSON structure received." >&2
    exit 1
fi

# データの抽出と保存

jq -c '.data[] | {id: .id, val: .value}' "$TMP_FILE" >> "$OUT_FILE"

echo "Process completed successfully."

systemd による定期実行の設定

バッチ処理を確実に実行し、ログを journald で管理するために systemd を活用します。

# /etc/systemd/system/metric-collector.service

[Unit]
Description=Metric Collector Batch Job
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/metric-collector.sh
User=batchuser
Group=batchuser
PrivateTmp=true

# /etc/systemd/system/metric-collector.timer

[Unit]
Description=Run Metric Collector every hour

[Timer]
OnCalendar=hourly
Persistent=true

[Install]
WantedBy=timers.target

【検証と運用】

  1. 構文チェック: bash -n script.sh で基本的な構文を確認。

  2. 正常系テスト: スクリプトを手動実行し、終了ステータス $?0 であること、および $OUT_FILE が生成されていることを確認。

  3. 異常系テスト: API_URL を無効なURLに変更し、trap が作動して一時ファイルが削除されるか確認。

  4. ログ確認: systemd経由での実行ログを確認。

    journalctl -u metric-collector.service -f
    

【トラブルシューティングと落とし穴】

  • 環境変数の欠落: systemdcron で実行する場合、ユーザーの .bashrc は読み込まれません。必要なパス(/usr/local/bin 等)はスクリプト内で明示するか、systemdの Environment 項目で指定してください。

  • 部分的な書き込み: パイプライン途中でディスクフルが発生した場合、set -o pipefail がないとエラーを見逃す可能性があります。必ず設定に含めてください。

  • sudo の扱い: スクリプト内で sudo を多用すると、非対話型実行時にパスワード入力で停止します。NOPASSWD 設定を行うか、実行ユーザーの権限を適切に設計(最小権限の原則)してください。

【まとめ】 運用の冪等性と堅牢性を維持するための3つの重要ポイント:

  1. 即時停止の原則: set -euo pipefail を冒頭に記述し、エラーを隠蔽しない。

  2. 自動クリーンアップ: trap を用いて、異常終了時でもゴミファイルやロックファイルを残さない。

  3. 入力の検証: jqgrep を駆使し、後続処理に不正なデータを渡さない。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました