シェルスクリプトのエラー通知設計|mailx・sendmail・Webhookで障害をすぐ知らせる仕組みの作り方

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)シェルスクリプト > シェルスクリプトのエラー通知設計|mailx・sendmail・Webhookで障害をすぐ知らせる仕組みの作り方
「夜中の2時に実行されているcronのバックアップスクリプトが失敗していた。気づいたのは翌朝9時。すでに24時間分のバックアップが取れていない状態で業務が始まっていた」

これは特別なケースではなく、自動化が進んだサーバー運用現場では日常的に起きる「静かな失敗」の典型例です。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 アドレスを明示したい場合に使う


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

自動化スクリプトが「静かに失敗する」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

Ubuntu 24.04 LTS:

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/

このスクリプトを実際の検証サーバー(Rocky Linux 9.4)で実行した際、rsyncが接続先不通で終了コード23を返したケースのメール本文:

スクリプト: 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 # --- 実際の処理 ---

Slackに届く通知の例:

: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

sendmailはPostfixなどのローカルMTAが稼働していないと機能しません。MTAの設定と確認方法についてはPostfix の基本設定の解説を参照してください。

実務で使える統合通知ライブラリの設計

メールと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/

外部SMTPリレー経由でメールを送信したい場合(ローカルMTAを経由せずGmailなどに直接中継する構成)は、Postfix リレー設定の詳細も参照してください。

うまく動かない時のトラブルシュート

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"

ステータスが200以外の場合、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 複数スクリプトで通知ロジックを共通化
エラー通知設計の核心は「スクリプトが自力でエラーを報告する仕組みを持つ」ことです。cronのMAILTO任せでは不十分で、trap ERR + 通知関数の組み合わせが現場の基本設計になります。

まず既存スクリプトに set -euo pipefailtrap 'notify_error ...' ERR の2行を追加するところから始めてみてください。深夜のcronエラーにその日中に気づけるようになるだけで、運用の安心感は大きく変わります。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。