本記事は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
【検証と運用】
構文チェック:
bash -n script.shで基本的な構文を確認。正常系テスト: スクリプトを手動実行し、終了ステータス
$?が0であること、および$OUT_FILEが生成されていることを確認。異常系テスト:
API_URLを無効なURLに変更し、trapが作動して一時ファイルが削除されるか確認。ログ確認: systemd経由での実行ログを確認。
journalctl -u metric-collector.service -f
【トラブルシューティングと落とし穴】
環境変数の欠落:
systemdやcronで実行する場合、ユーザーの.bashrcは読み込まれません。必要なパス(/usr/local/bin等)はスクリプト内で明示するか、systemdのEnvironment項目で指定してください。部分的な書き込み: パイプライン途中でディスクフルが発生した場合、
set -o pipefailがないとエラーを見逃す可能性があります。必ず設定に含めてください。sudo の扱い: スクリプト内で
sudoを多用すると、非対話型実行時にパスワード入力で停止します。NOPASSWD設定を行うか、実行ユーザーの権限を適切に設計(最小権限の原則)してください。
【まとめ】 運用の冪等性と堅牢性を維持するための3つの重要ポイント:
即時停止の原則:
set -euo pipefailを冒頭に記述し、エラーを隠蔽しない。自動クリーンアップ:
trapを用いて、異常終了時でもゴミファイルやロックファイルを残さない。入力の検証:
jqやgrepを駆使し、後続処理に不正なデータを渡さない。

