---
title: "ターミナルの長いコマンドが終わったら通知する"
description: "40 分かかるビルドに付き添うのはもうやめましょう。どんなコマンドの後ろにも 1 行足すだけで、結果がスマートフォンに届きます。すでに席を離れていても。"
date: 2026-09-09
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - ターミナル
  - シェル
  - ビルド通知
  - AI コーディングエージェント
  - Webhook 通知
---

# ターミナルの長いコマンドが終わったら通知する

学習の実行、フルテスト、`docker build`、あるいは AI コーディングエージェントに長いタスクを任せる。そのあと選べるのは、どちらも良くない 2 つだけです。プログレスバーを眺めて座っているか、席を離れて 20 分後に戻り、実は 90 秒で失敗していたと知るか。

3 つ目の選択肢があります。1 行で済みます。

## その 1 行

Echobell でチャンネルを作り、webhook URL をコピーして、実行するコマンドの後ろに付けます。

```bash
pnpm build; curl -sS -X POST https://hook.echobell.one/t/YOUR_TOKEN \
  -H 'content-type: application/json' \
  -d '{"title":"ビルド終了","body":"echobell-web"}'
```

`&&` ではなく `;` である点に注意してください。`&&` にすると成功したときだけ通知が飛びます。これはちょうど逆で、失敗こそがいちばん知りたい状況です。

## 終了コードも一緒に送る

「終わりました」だけの通知は、話の半分でしかありません。ステータスも取りましょう。

```bash
pnpm build; s=$?; curl -sS -X POST https://hook.echobell.one/t/YOUR_TOKEN \
  -H 'content-type: application/json' \
  -d "{\"title\":\"ビルド $([ $s -eq 0 ] && echo 成功 || echo 失敗)\",\"status\":\"$s\"}"
```

`s=$?` はコマンドの直後に置く必要があります。あいだに何かを挟むと（`[` テスト自体を含めて）`$?` が上書きされます。

## シェル関数にする

毎回これを打っていては本末転倒です。`~/.zshrc` か `~/.bashrc` に次を入れておきましょう。

```bash
notify() {
  "$@"
  local status=$?
  curl -sS -X POST "$ECHOBELL_HOOK" \
    -H 'content-type: application/json' \
    -d "{\"command\":\"$*\",\"status\":\"$status\",\"host\":\"$(hostname -s)\"}" \
    >/dev/null
  return $status
}
```

`ECHOBELL_HOOK` はシェルのプロファイルで設定します。さらに良いのは、dotfiles のリポジトリには一切入れず、コミットしないファイルから export することです。あとはこうするだけ。

```bash
notify pnpm test
notify cargo build --release
notify python train.py
```

最後の `return $status` は重要です。これがあることで `notify` は透過的に振る舞い、`notify make && ./deploy.sh` も期待どおり動きます。

この payload に対して、チャンネルのテンプレートはこう書けます。

**タイトル**

```
{{command}} · {{status}}
```

**本文**

```
{{host}} にて
```

## 失敗したときだけ鳴らしてほしい

たいていの実行は成功しますし、それをいちいち知る必要はありません。チャンネルを 2 つに分けるときれいに解決します。すべてを受ける通常チャンネルと、条件を付けた着信チャンネルです。

```
status != "0"
```

ベッドから起きてでも対応したいものだけ 2 つ目のチャンネルに向ければ、成功した実行は静かに過ぎていきます。

## ノート PC を閉じても、SSH が切れても届くように

コマンドを SSH 越しに走らせている場合、ノート PC を閉じるとシェルごと落ちて通知は永遠に飛びません。対処は 2 つ。

**tmux** —— セッション内で起動してデタッチします。

```bash
tmux new -d -s build 'notify pnpm build'
```

**nohup** —— 単発なら。

```bash
nohup bash -c 'notify pnpm build' >/dev/null 2>&1 &
```

どちらでもプロセスは接続より長く生き残り、あなたがつないでいるかどうかに関係なく通知は届きます。

## AI コーディングエージェント

同じやり方は、長いタスクに取り組む CLI エージェントにもそのまま使えます。

```bash
notify codex exec "決済モジュールをリファクタしてテストを流して"
```

途中で止まって人を待つエージェント（承認待ち、権限確認待ち）には、完了通知は誤った道具です。まだ完了していないからです。その場合はエージェント自身の hook やコールバックが必要で、プッシュよりも電話がふさわしい。[エージェントの承認待ちを電話に変える](/ja/blog/ai-agent-human-in-the-loop-alerts)がその話で、Claude Code の `Notification` hook と `agent_needs_input` matcher も扱っています。

両方使いましょう。「詰まった」は hook、「終わった」はシェル関数です。

## 通知に入れてはいけないもの

**コマンドの出力。** 失敗したビルドの末尾数行を本文に流し込みたくなりますが、こらえてください。ビルドログにはトークンや接続文字列、顧客データが、多くの人が思うより頻繁に含まれます。そして通知の本文はロック画面に出ます。終了コードだけ送って、ターミナルは自分で見に行きましょう。

**チャンネル URL を公開 dotfiles リポジトリに。** この URL を知っていれば誰でもあなたのチャンネルに投稿できます。バージョン管理しないファイルに置き、**POST のみ** を有効にして、リンクプレビューが誤って発火しないようにしてください。

## よくある質問

### `terminal-notifier` や `notify-send` ではだめですか？

あれはコマンドを実行しているマシン上に通知を出します。目の前に座っているなら十分です。この構成が狙っているのは、座っていないときです。リモートのマシン、閉じたノート PC、別の部屋。

### Windows でも動きますか？

考え方は同じで、書き方が違います。PowerShell なら `Invoke-RestMethod -Method Post -Uri $env:ECHOBELL_HOOK -ContentType application/json -Body $json` が相当し、`$?` の代わりに `$LASTEXITCODE` を使います。

### 時計にも届きますか？

届きます。購読はペアリング済みの Apple Watch にも配信され、「ビルドが終わった」を知るには正直これがいちばん適した形です。

### コマンドが 12 時間かかる場合は？

この構成にタイムアウトはありません。コマンドが返った時点で `curl` が走るだけです。接続が切れてもプロセスが道連れにならないよう、tmux の下で実行してください。

### チームメンバーも同じ通知を受け取れますか？

受け取れます。チャンネルを共有すれば、購読者ごとに通知タイプを選べます。共用の学習マシンで「GPU がまた空いた」を複数人が知りたい場合に便利です。

## まとめ

シェル関数ひとつ、チャンネルひとつ、失敗だけ受けたいなら条件をひとつ。設定に 2 分ほど、返ってくるのはプログレスバーを眺めていた 20 分です。

[iPhone 版 Echobell をダウンロード](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-notify-when-terminal-command-finishes-ja&mt=8)、または [Google Play で入手](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid)して、席を立つに足るくらい長いものでまず試してみてください。

---

## 関連記事

- [エージェントの承認待ちを電話に変える](/ja/blog/ai-agent-human-in-the-loop-alerts)
- [GitHub Actions の失敗を見逃さない](/ja/blog/github-actions-notifications)
- [cron ジョブ失敗の通知](/ja/blog/cron-job-failure-alerts)
- [Webhook トリガーのドキュメント](/ja/docs/webhook)
- [チャンネル条件のリファレンス](/ja/docs/conditions)
