これは特別なケースではなく、自動化が進んだサーバー運用現場では日常的に起きる「静かな失敗」の典型例です。cronで実行されるスクリプトがエラーで終了しても、誰かがログを確認しに行かない限り誰も気づきません。
この記事では、シェルスクリプト エラー通知の設計手法として、mailxによるメール送信・sendmailによるMTA直接利用・Slack Webhookによるリアルタイム通知の3方式を、RHEL 9.4 / Ubuntu 24.04 LTSで動作確認した実行例とともに解説します。trapコマンドと組み合わせた自動通知テンプレートも紹介するので、既存のcronスクリプトにすぐ組み込めます。
この記事のポイント
・シェルスクリプト エラー通知の最短実装は「echo 本文 | mailx -s 件名 宛先」の1行
・trap ERR でエラーを自動検知し、通知関数を呼び出すのが実務の基本設計
・Slack Webhook は curl 1コマンドで送れ、メールより即時性が高い
・sendmail はメールヘッダを自由に制御でき、From アドレスを明示したい場合に使う
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
自動化スクリプトが「静かに失敗する」3つの理由
cronによる自動実行スクリプトの障害が見逃される原因は、ほぼ以下の3パターンに集約されます。・cronのMAILTO設定が機能していない:cronにはMAILTO変数でメール通知する機能がありますが、ローカルMTAの設定が不完全で届かないことが多く、設定自体が形骸化しています
・スクリプトがエラーを握りつぶしている:if文や|| trueでコマンドの失敗を無視しているため、終了コードが正常のまま通り過ぎる
・ログはあるが誰も見ていない:ログファイルに記録しても、アクティブに監視していなければ障害発生から発見まで数時間~数日かかる
確実に障害を知らせるには、スクリプト自身が通知する仕組みを持つ設計が必要です。それがこの記事で解説するエラー通知設計の本質です。
mailxコマンドでエラーをメール通知する
mailxはLinuxサーバーで最もよく使われているメール送信コマンドです。ローカルMTA(Postfixなど)と連携して動作し、シンプルな構文でスクリプトからメールを送れます。1. mailxのインストールと動作確認
RHEL 9.4 / Rocky Linux 9 系:# RHEL 9系ではs-nailパッケージがmailxコマンドを提供する dnf install mailx -y
apt install mailutils -y
echo "テスト送信" | mailx -s "mailxテスト" admin@example.com
2. スクリプトからメールを送る基本形
最もシンプルな実装は以下の1行です。ここからすべての応用が始まります。echo "バックアップが失敗しました" | mailx -s "[ALERT] backup.sh エラー" admin@example.com
mailx -s "[ALERT] backup.sh エラー" admin@example.com << MAIL_BODY スクリプト: backup.sh サーバー: $(hostname) 発生日時: $(date '+%Y-%m-%d %H:%M:%S') ログ: /var/log/backup.log MAIL_BODY
3. trapコマンドと組み合わせた自動通知テンプレート
スクリプトに毎回 if 文を書くのではなく、trap ERR を使うとコマンドが失敗した瞬間に自動で通知を飛ばせます。#!/bin/bash set -euo pipefail ALERT_TO="admin@example.com" SCRIPT_NAME=$(basename "$0") SERVER_HOST=$(hostname) notify_by_mail() { local exit_code=$1 local line_no=$2 { echo "スクリプト: ${SCRIPT_NAME}" echo "サーバー: ${SERVER_HOST}" echo "失敗した行: ${line_no} 行目" echo "終了コード: ${exit_code}" echo "発生日時: $(date '+%Y-%m-%d %H:%M:%S')" echo "" echo "ログを確認してください: /var/log/${SCRIPT_NAME%.sh}.log" } | mailx -s "[ALERT] ${SCRIPT_NAME} がエラー終了 @ ${SERVER_HOST}" "$ALERT_TO" } trap 'notify_by_mail $? $LINENO' ERR # --- 実際の処理(失敗するとtrapが自動発動する)--- rsync -avz /data/src/ backup-server:/mnt/backup/ tar czf /backup/db-$(date +%Y%m%d).tar.gz /var/lib/mysql/
スクリプト: backup.sh サーバー: web01.example.com 失敗した行: 28 行目 終了コード: 23 発生日時: 2026-07-20 02:35:17 ログを確認してください: /var/log/backup.log
set -euo pipefail と組み合わせることで、パイプライン途中の失敗も含めてすべてのエラーをtrapで拾えます。Slack Webhookでリアルタイム通知する
メール通知は確実ですが、気づくまでに時間がかかります。Slack通知はスマートフォンにプッシュ通知が届くため、深夜のエラーにも即座に対応できます。1. Slack Incoming Webhookの準備
Slackの「App管理」→「Incoming Webhooks」でWebhook URLを発行します。URLはhttps://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXXX の形式です。セキュリティ上の注意点として、Webhook URLを平文ファイルやスクリプト内にハードコードするのは避け、環境変数または権限を絞った設定ファイルで管理してください。
# /etc/scripts/notify.conf (パーミッション: 600, root所有) SLACK_WEBHOOK_URL="https://hooks.slack.com/services/TXXXX/BXXXX/XXXXXXXX" ALERT_EMAIL="admin@example.com"
2. curlでSlack通知を送るシェル関数
send_slack_alert() { local message="$1" [[ -z "${SLACK_WEBHOOK_URL:-}" ]] && return 0 curl -s -X POST \ -H 'Content-type: application/json' \ --data "{\"text\":\":x: ${message}\"}" \ "$SLACK_WEBHOOK_URL" > /dev/null } # 使用例 send_slack_alert "バックアップが失敗しました (サーバー: $(hostname), 日時: $(date '+%Y-%m-%d %H:%M:%S'))"
3. trapと組み合わせた実用例
#!/bin/bash set -euo pipefail source /etc/scripts/notify.conf SCRIPT_NAME=$(basename "$0") SERVER_HOST=$(hostname) notify_slack_error() { local exit_code=$1 local line_no=$2 local msg=":x: *[ALERT]* \`${SCRIPT_NAME}\` が \`${SERVER_HOST}\` でエラー終了" msg="${msg} (行: ${line_no}, 終了コード: ${exit_code})" curl -s -X POST -H 'Content-type: application/json' \ --data "{\"text\":\"${msg}\"}" \ "$SLACK_WEBHOOK_URL" > /dev/null } trap 'notify_slack_error $? $LINENO' ERR # --- 実際の処理 ---
:x: [ALERT] `backup.sh` が `web01.example.com` でエラー終了 (行: 35, 終了コード: 23)sendmailコマンドによるメール通知(ヘッダ制御が必要な場合)
mailx は手軽ですが、From アドレスやContent-Typeヘッダを細かく制御したい場合は /usr/sbin/sendmail を直接呼び出す方法が有効です。文字コードをUTF-8と明示したい場合や、Return-Pathを設定してメールフィルタを通過させたい場合に使います。#!/bin/bash MAIL_TO="admin@example.com" MAIL_FROM="noreply@web01.example.com" SCRIPT_NAME=$(basename "$0") HOST=$(hostname) DATE=$(date '+%Y-%m-%d %H:%M:%S') /usr/sbin/sendmail "$MAIL_TO" << MAIL_BODY From: ${MAIL_FROM} To: ${MAIL_TO} Subject: [ALERT] ${HOST} でスクリプトエラーが発生しました MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 ${DATE} に ${HOST} でエラーが発生しました。 スクリプト名: ${SCRIPT_NAME} ログ確認先: /var/log/${SCRIPT_NAME%.sh}.log MAIL_BODY
実務で使える統合通知ライブラリの設計
メールとSlack通知を1つの関数にまとめてライブラリ化すると、複数スクリプトから共通の通知処理を呼び出せます。新しいスクリプトを書くたびに通知ロジックを実装し直す必要がなくなります。1. 通知ライブラリ(/opt/scripts/lib/notify.sh)
#!/bin/bash # /opt/scripts/lib/notify.sh --- エラー通知ライブラリ # デフォルト値(呼び出し元でexportして上書き可) ALERT_EMAIL="${ALERT_EMAIL:-admin@example.com}" SLACK_WEBHOOK_URL="${SLACK_WEBHOOK_URL:-}" notify_error() { local msg="$1" local host host=$(hostname) local ts ts=$(date '+%Y-%m-%d %H:%M:%S') local full_msg="[${ts}] [ERROR] ${host}: ${msg}" # メール通知(常に送信) echo "$full_msg" | mailx -s "[ALERT] ${host} スクリプトエラー" "$ALERT_EMAIL" # Slack通知(Webhook URLが設定されている場合のみ) if [[ -n "$SLACK_WEBHOOK_URL" ]]; then curl -s -X POST -H 'Content-type: application/json' \ --data "{\"text\":\":x: ${full_msg}\"}" \ "$SLACK_WEBHOOK_URL" > /dev/null fi }
2. 既存スクリプトへの組み込み方法
既存スクリプトへの追加は最小2行で完結します。#!/bin/bash set -euo pipefail # 追加1行目: ライブラリ読み込み source /opt/scripts/lib/notify.sh # 追加2行目: エラー時に自動通知するtrap trap 'notify_error "$(basename "$0") が行 ${LINENO} で失敗 (終了コード: $?)"' ERR # --- 既存の処理(変更不要)--- tar czf /backup/data-$(date +%Y%m%d).tar.gz /var/www/html/ rsync -az /backup/ backup-server:/mnt/nfs/backup/
うまく動かない時のトラブルシュート
1. メールが届かない
最も多い原因はローカルMTAが停止しているか、外部メールサーバーへの中継設定が未完了のケースです。# Postfixの稼働状態を確認 systemctl status postfix # 出力例(正常時) * postfix.service - Postfix Mail Transport Agent Loaded: loaded (/usr/lib/systemd/system/postfix.service; enabled) Active: active (running) since Sun 2026-07-20 01:00:00 JST
active (running) でなければ systemctl start postfix で起動します。送信キューに滞留しているかの確認とエラーログは以下で確認できます:# 送信キューの確認 mailq # メールログの確認(RHEL系) tail -f /var/log/maillog # メールログの確認(Debian/Ubuntu系) tail -f /var/log/mail.log
2. Slack通知が届かない
curlのHTTPステータスを明示的に確認します:curl -w "\nHTTP Status: %{http_code}\n" \ -X POST -H 'Content-type: application/json' \ --data '{"text":"疎通テスト"}' \ "$SLACK_WEBHOOK_URL"
-x http://proxy.example.com:8080 オプションを追加します。3. trapが発動しない
if 文の条件として評価されたコマンドが失敗しても、trap ERR は発動しません。これはbashの仕様です:# if文の条件では失敗してもtrapは発動しない(仕様) if ls /nonexistent_path; then echo "found" fi # trapを確実に発動させるには明示的な終了コードチェックを使う ls /nonexistent_path || { notify_error "ディレクトリが見つかりません"; exit 1; }
set -e を使う場合、サブシェル内のコマンド失敗は親シェルのtrapで拾えないことがあります。処理をサブシェルで実行している場合は、サブシェル内にもtrapを設定するか、終了コードを明示的に確認してください。エラー通知設計をさらに深めてシェルスクリプトの設計力を体系的に身につけたい方は、シェルスクリプト実践講座(Linux Master Pro)もご覧ください。現場で使えるシェルスクリプト設計を体系的に学べます。
本記事のまとめ
| 通知方式 | 代表的なコマンド形式 | 特徴・向いている場面 |
|---|---|---|
| mailxメール送信 | echo 本文 | mailx -s 件名 宛先 |
ローカルMTA経由・シンプルで確実 |
| sendmail直接利用 | /usr/sbin/sendmail 宛先 << HEREDOC |
ヘッダ自由制御・文字コード明示 |
| Slack Webhook | curl -X POST --data '{"text":"..."}' URL |
即時通知・スマートフォン連携 |
| trap ERR連携 | trap '通知関数 $? $LINENO' ERR |
エラー自動検知・自動通知 |
| 統合通知ライブラリ | source /opt/scripts/lib/notify.sh |
複数スクリプトで通知ロジックを共通化 |
trap ERR + 通知関数の組み合わせが現場の基本設計になります。まず既存スクリプトに
set -euo pipefail と trap 'notify_error ...' ERR の2行を追加するところから始めてみてください。深夜のcronエラーにその日中に気づけるようになるだけで、運用の安心感は大きく変わります。
シェルスクリプト講座を見る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 前のページへ:自作シェルスクリプトをsystemdサービス化して常駐運用する方法|Unitファイル作成・Restart設定・ログ確認
- この記事の属するカテゴリ:シェルスクリプトへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます