シェルスクリプトから iPhone に通知する 5 つの方法

公開日:2026-11-08

コマンドの終了を知らせるだけなら、メール、Slack や Discord の webhook、Pushover、ntfy、Nodd のどれでも curl 1 行で送れます。違いが出るのは、スクリプトが人に確認して答えを受け取りたいときです。Nodd の nodd ask は iPhone での承認を待ち、承認で 0、却下で 10、応答なしで 11 の終了コードを返します。

結論

コマンドの終了をスマホで知りたいだけなら、どの方法でも curl 1 行で足ります。すでに Slack や Discord を使っているなら incoming webhook、Android やデスクトップにも届けたいなら Pushover か ntfy が手軽です。スクリプトが「続けてよいか」を人に聞き、その答えで処理を分けたいときは、答えがスクリプトに返る仕組みが必要です。Nodd はこの用途のためのアプリで、nodd ask が承認を待ち、終了コードで結果を返します。

通知がほしい場面と、返事がほしい場面

長いビルド、バックアップ、データの移行、機械学習の学習などは、終わるまで画面の前で待つ理由がありません。終わったときや失敗したときにスマホへ届けば、ほかの作業ができます。この段階では「届くこと」だけが要件です。

もう一歩進むと、スクリプトの途中で人の判断がほしくなります。たとえば「本番 DB の移行を始めてよいか」「どちらの環境へ反映するか」です。ここでは、通知が届くだけでは足りません。押されたボタンがスクリプトに返り、承認なら続け、却下や無応答なら止める必要があります。無応答を承認と扱わないことも大事です。

以下では、この 2 つの要件で 5 つの方法を比べます。

5 つの方法

1. メール(mail、sendmail、curl の SMTP)

いちばん古くからある方法です。サーバーにメールを送る仕組みがあれば、追加のサービスは要りません。

# mail コマンド(送信できるメールサーバーの設定が必要)
echo "backup.sh が終わりました" | mail -s "backup done" you@example.com

# curl で SMTP サーバーへ直接送る(email.txt は To、From、Subject の行と本文)
curl smtp://mail.example.com --mail-from bot@example.com \
  --mail-rcpt you@example.com --upload-file email.txt

設定の手間は環境しだいです。送信できるメールサーバーや中継の設定がないと届かず、迷惑メールに振り分けられることもあります。スマホのメールアプリの通知に頼るので、すぐ気づけるとは限りません。返事を受け取ってスクリプトに戻す仕組みはありません。本文はメールとして経路上のサーバーとメールボックスに残ります。

2. Slack や Discord の incoming webhook

チャンネルごとに発行した URL へ JSON を POST するだけです。チームの全員が同じチャンネルで見られます。

# Slack
curl -X POST -H 'Content-type: application/json' \
  --data '{"text":"make test が終わりました"}' \
  "$SLACK_WEBHOOK_URL"

# Discord
curl -H 'Content-Type: application/json' \
  -d '{"content":"make test が終わりました"}' \
  "$DISCORD_WEBHOOK_URL"

URL を作れば、すぐ使えます。スマホにはそれぞれのアプリの通知として届きます。Slack のボタンの押下を受け取るには、Slack アプリで Interactivity を有効にし、Slack から届く POST を受ける公開の Request URL を用意する必要があります。Discord は、アプリが持たない webhook ではボタンなどの操作できる部品を送れません。つまり webhook だけでは返事は戻りません。本文はそのサービスの中に会話として残ります。

3. Pushover

通知を届けるための専用サービスです。iPhone、iPad、Android、デスクトップのアプリがあります。

curl -s https://api.pushover.net/1/messages.json \
  --form-string "token=$PUSHOVER_APP_TOKEN" \
  --form-string "user=$PUSHOVER_USER_KEY" \
  --form-string "message=make test が終わりました"

アプリのトークンとユーザーキーを作れば使えます。priority=2(緊急)では、受け取った人が確認するまで retry の間隔で通知をくり返し、レスポンスの receipt で確認されたかどうかを問い合わせるか、callback の URL で受け取れます。分かるのは「確認したかどうか」で、承認か却下かのような答えを返す仕組みではありません。通知の補足として URL を 1 つ添えられます。

4. ntfy

オープンソースの通知サービスです。公開サーバーの ntfy.sh を使うことも、自分のサーバーで動かすこともできます。

curl -H "Title: Build finished" -H "Priority: high" \
  -d "make test が終わりました" ntfy.sh/your-hard-to-guess-topic

登録なしで、トピック名を決めて送るだけで始められます。そのかわり、ntfy のドキュメントのとおりトピック名がパスワードの役割をするので、推測されにくい名前にします。認証が必要なサーバーでは、アクセストークンを Authorization: Bearer で送ります。通知には最大 3 つのアクションボタンを付けられ、http のボタンは押されたときに指定の URL へ HTTP リクエストを送ります。これで返事を受ける仕組みは作れますが、受け口のサーバーは自分で用意し、スクリプトはそこから結果を受け取る必要があります。サーバーは既定でメッセージを 12 時間保持します。自前のサーバーなら本文は自分の管理下に置けます。

5. Nodd

スクリプトや CI、AI エージェントからの通知と確認を iPhone に届けるアプリです。送信キーで API に送ります。

curl https://api.nodd.blipdrop.net/v1/messages \
  -H "Authorization: Bearer $NODD_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind":"notice","title":"make test が終わりました"}'

アプリで Apple のサインインをし、チャンネルを作って送信キーを発行すれば使えます。kind を approval にすると承認と却下のボタンが付き、Face ID で答えます。答えは状態の取得、callback、イベント API で受け取れ、CLI の nodd ask なら終了コードになります。本文は受け取る端末ごとの公開鍵で暗号化して預かり、端末が受け取ると消えます。ただし暗号化するまでの処理ではサーバーが本文を扱います。iPhone 専用です。

比較表

メール設定:メールサーバーしだい。返事:戻らない。本文:経路とメールボックスに残る
Slack / Discord の webhook設定:URL を作るだけ。返事:webhook だけでは戻らない(Slack はアプリと公開の受け口が必要)。本文:サービスに会話として残る
Pushover設定:トークンとユーザーキー。返事:緊急の優先度で「確認したか」を receipt と callback で受け取れる。承認と却下の区別はない。iPhone、Android、デスクトップ
ntfy設定:トピック名だけで始められる。自前のサーバーも可。返事:http のボタンで自分の URL を呼べるが、受け口は自作。iPhone、Android、Web
Nodd設定:アプリでチャンネルと送信キー。返事:承認、却下、選択肢が送った側に返る。Face ID で答える。本文は端末ごとに暗号化し、受け取ると消える。iPhone 専用

終わったことを知るだけなら、すでに使っている道具で十分です。Android やデスクトップにも届けたい場合や、サーバーを自分で持ちたい場合は、Pushover や ntfy が合います。両者の違いは Pushover・ntfy と Nodd の違いにまとめました。

Nodd で終了を知らせる

CLI を入れてログインします。nodd login で QR コードが出るので、アプリの「設定 → CLI にログイン」で読み取り、Face ID で許可します。CI ではログインせず、環境変数 NODD_KEY に送信キーを入れます。

brew install blipdrop/tap/nodd
nodd login

成功と失敗で重要度を変える例です。--level は low、normal、high で、iOS の Passive、Active、Time Sensitive に対応します。--body - は本文を標準入力から読みます。本文は 1,000 文字までなので、ログは末尾だけを渡します。

#!/bin/sh
./long_task.sh > task.log 2>&1
rc=$?
if [ "$rc" -eq 0 ]; then
  nodd send "long_task が終わりました" --level low
else
  tail -c 900 task.log | nodd send "long_task が失敗しました (exit $rc)" --level high --body -
fi

CLI を入れられない環境では、上の curl のまま "level":"high" を足せば同じです。API は受け付けると 202 を返しますが、これは端末に届いたことの保証ではありません。毎晩動くジョブなら、終わったときの通知より「来るはずの報告が来ない」ことを知らせる nodd heartbeat が向いています。cron ジョブの見張りをご覧ください。

スクリプトで承認を待って分岐する

nodd ask は承認を求めて待ち、結果を終了コードで返します。

0承認された、または選択肢が選ばれた
10却下された
11応答なしで終わった(期限切れ、取り下げ、失敗)
12待っているあいだに時間切れになった
1そのほかの失敗

いちばん短い形は nodd ask "本番へ反映しますか" && ./deploy.sh です。承認のときだけ次へ進みます。理由ごとに分けたいときは、終了コードを変数に受けます。set -e のスクリプトでは、|| rc=$? を付けないと却下の時点でスクリプトが終わります。

#!/bin/sh
set -e
rc=0
nodd ask "本番 DB の移行を始めますか" --body "users テーブルに列を追加します" --expires-in 1800 || rc=$?

case "$rc" in
  0)  ./migrate.sh ;;
  10) echo "却下されました" >&2; exit 1 ;;
  11) echo "応答がありませんでした" >&2; exit 1 ;;
  12) echo "待ち時間が切れました" >&2; exit 1 ;;
  *)  echo "nodd が失敗しました" >&2; exit 1 ;;
esac

標準出力には approved、rejected、選んだ選択肢の ID、または expired などの状態が出ます。却下の理由があれば標準エラー出力へ Note: 理由 と出るので、標準出力はそのまま判定に使えます。--option を 2 から 4 回指定すると、承認ではなく選択になります。

target=$(nodd ask "どこへ反映しますか" --option staging=Staging --option prod=Production)
./deploy.sh "$target"

--expires-in は応答期限の秒数で、既定は 3600 です。CLI が待つのは応答期限の 15 分後までで、通信の失敗では諦めず、確定した応答だけを結果にします。--no-wait で送るだけにして、あとで nodd wait ID で待つこともできます。

CLI を使わずに curl と jq で書くこともできます。応答があるまでは action_state が open のままで、状態の取得には Retry-After: 3 が付くので、3 秒おきに問い合わせます。

id=$(curl -s https://api.nodd.blipdrop.net/v1/messages \
  -H "Authorization: Bearer $NODD_KEY" -H "Content-Type: application/json" \
  -d '{"kind":"approval","title":"本番 DB の移行","expires_in":1800}' | jq -r .id)

while :; do
  r=$(curl -s "https://api.nodd.blipdrop.net/v1/messages/$id" -H "Authorization: Bearer $NODD_KEY")
  [ "$(echo "$r" | jq -r .action_state)" = open ] || break
  sleep 3
done

# approve のときだけ進む。応答なしを承認と扱わない
[ "$(echo "$r" | jq -r '.response.option_id // empty')" = approve ] && ./migrate.sh

このループは通信に失敗すると止まり、移行はしません。待ち方の細かい扱いは CLI が引き受けるので、ふつうは nodd ask を使うほうが確実です。項目の一覧は CLI と API のドキュメントにあります。

注意点と制限

よくある質問

コマンドが終わったことをスマホに知らせる、いちばん簡単な方法は何ですか?
すでに Slack や Discord を使っているなら、incoming webhook の URL へ curl で POST するのが簡単です。専用のアプリを入れるなら、Pushover、ntfy、Nodd のいずれも curl 1 行で送れます。
通知のボタンを押した結果をスクリプトで受け取れますか?
Nodd では受け取れます。nodd ask は承認で 0、却下で 10、応答なしで 11 を返します。Slack や ntfy でも、ボタンの押下を受けるサーバーを自分で用意すれば作れます。
応答がないとき、スクリプトはどうなりますか?
nodd ask は応答期限が過ぎると 11 を、待ち時間が切れると 12 を返します。どちらも 0 ではないので、nodd ask の後に続ける形なら処理は進みません。
Android でも使えますか?
Nodd は iPhone 専用です。Android やデスクトップに届けたい場合は、Pushover か ntfy が向いています。

Nodd は、スクリプトや CI、AI エージェントからの確認を iPhone に届け、Face ID での承認を送った側のプログラムへ返すアプリです。無料プランがあります。

関連する記事