シェルスクリプトから 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 のドキュメントにあります。
注意点と制限
- Nodd は iPhone 専用で、iOS 17.0 以降が必要です。現在、EU 域内では配信していません。Android やデスクトップにも届けたいなら Pushover か ntfy を選んでください
- 無料プランは 1 人、端末 1 台、月 1,000 通、受け取り期限 24 時間、同時の応答待ち 3 件です。応答期限も受け取り期限を超えられません
- 本文はサーバーが暗号化するまでの処理で扱います。metadata、応答、却下の理由は端末ごとの暗号化の対象外です。パスワードや API キーを本文に入れないでください
--markdownと--imageの表示、--inputと--secretへの回答には Nodd 1.1 以降のアプリが必要です- 応答がないまま期限が来たら、処理を止める側に倒してください。
nodd askは応答なしで 0 を返しません
よくある質問
- コマンドが終わったことをスマホに知らせる、いちばん簡単な方法は何ですか?
- すでに 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 での承認を送った側のプログラムへ返すアプリです。無料プランがあります。