サーバーで 30 分かかるビルドを走らせたら、終わったときに手元のデスクトップ通知がほしい。SSH 先の vim でコピーしたテキストを、手元のブラウザーにそのまま貼りたい。ターミナルのウィンドウタイトルが、いつのまにか勝手に変わっている。エラーメッセージに出てくるオプション名をクリックすると、ドキュメントが開く。こうしたことの裏にあるのは同じしくみで、プログラムが出力に混ぜてターミナルに送る指示です。その中でよく使われる種類が OSC です。この記事では、OSC とは何か、何ができるか、どう使うかを紹介します。
プログラムはどうやってターミナルに指示するか
ターミナルの中で動くプログラムにできるのは、画面に文字を出力することだけです。文字を赤くする、ウィンドウタイトルを変えるといった、それ以外のことをターミナルにさせたいときは、出力の中に特別な文字の並びを短く混ぜます。先頭は ESC という目に見えない文字です。ターミナルはこれを見つけると、表示はせず、書かれたとおりに動きます。これを「エスケープシーケンス」と呼び、画面の色やカーソルの移動もこのしくみで動いています。
OSC はその一種で、Operating System Command(オペレーティングシステムコマンド)の略です。画面の文字以外のこと、つまりウィンドウタイトル、クリップボード、デスクトップ通知、リンクを受け持ちます。OSC には何をするかを表す番号があり、そのあとにタイトルの文字列や通知の本文といった中身が続きます。
SSH を使う人にとってありがたいのは、OSC がただの出力だという点です。サーバー上のプログラムがほかの文字といっしょに出力すると、SSH を通ってそのまま手元のターミナルに届き、別の通信路はいりません。逆向きに、キー入力をプログラムに伝える方法は別の取り決めで、kitty キーボードプロトコルで扱っています。同じ接続でファイルを送る方法は rz / sz と ZMODEM を参照してください。
OSC でできること
よく使うものは次のとおりです。
| 番号 | できること |
|---|---|
| 0、2 | ウィンドウやタブのタイトルを変える |
| 7 | 今いるディレクトリをターミナルに伝え、同じ場所で新しいタブを開けるようにする |
| 8 | 文字列をクリックできるリンクにする |
| 9、777 | デスクトップ通知を出す |
| 52 | 手元のクリップボードに内容を入れる |
これらはひとつの規格から来たものではありません。7、8、9、777 はあとから各ターミナルが加えたもので、対応状況はターミナルによって違います。ターミナルは知らないシーケンスを、ふつうは単に無視します。
ウィンドウタイトルが勝手に変わるのはなぜでしょうか。多くのシェル設定は、プロンプトを出すたびに OSC でタイトルを「ユーザー@ホスト:ディレクトリ」のような内容にします。Claude Code のように、動いているあいだ自分のタイトルに変えるプログラムもあります。
リンク(OSC 8)は Web ページのリンクと同じで、見えているのは文字列、その裏にアドレスがあります。ls --hyperlink が出すファイル名や、GCC 10 以降のエラーメッセージに出てくるオプション名が、このリンクです。仕様は、ふつうのクリックではなく、修飾キーを押しながらのクリックや右クリックメニューで開くようターミナルに求めています。
コマンドが終わったら通知する
通知の番号は 2 つあります。OSC 9 は iTerm2 が定義したもので、本文だけを持ちます。OSC 777 は rxvt-unicode(urxvt)に由来し、本文とは別にタイトルを持てます。foot、WezTerm、Ghostty、ShellRS は両方に対応し、iTerm2 と kitty は OSC 9 に、rxvt-unicode は OSC 777 に対応しています。
使い方は簡単で、コマンドのあとにつなげるだけです。
make; printf '\e]9;ビルド完了\a'
make を実行したいコマンドに置き換えます。「ビルド完了」が通知の本文です。&& ではなく ; にしておけば、失敗したときにも通知が来ます。ターミナルが 777 にしか対応していなければ、printf '\e]777;notify;タイトル;本文\a' にします。両方を同時に送る必要はありません。両方に対応したターミナルでは通知が 2 回出ることがあります。
リモートのサーバー上でもそのまま使えます。通知は出力といっしょに SSH を通って手元に戻り、手元のデスクトップに出ます。サーバーには何もインストールする必要がありません。コマンドを走らせたらほかの作業をしていても、終わったときに知らせてくれます。
サーバーでコピーして、手元に貼る
SSH 先の vim で y を押してコピーし、手元で ⌘V か Ctrl+V を押しても、何も貼り付けられません。内容はサーバー上の vim の中にあるだけで、手元のクリップボードはそれを知らないからです。
そのためにあるのが OSC 52 です。プログラムがコピーしたい内容を OSC 52 に入れて出力すると、ターミナルがそれを受け取り、手元のクリップボードに書き込みます。vim に OSC 52 のプラグインを入れるか、tmux で set-clipboard を有効にすれば(次の節を参照)、コピーした内容がそのまま手元に届きます。
OSC 52 は逆向きにも使え、クリップボードの中身をプログラムに渡すようターミナルに頼めます。これは危険です。ターミナルに出力できるプログラムなら、リモートのサーバー上のものでも、コピーしたばかりのパスワードをこっそり読めてしまうからです。そのためターミナルは読み取りに慎重で、許可しないものもあれば、先に確認するものもあります。書き込みは、最悪でもクリップボードが書き換わるだけなのでずっと安全で、多くのターミナルが既定で許可しています。先に設定で有効にする必要があるものもあります。ただ、出どころの怪しいファイルを cat しただけでクリップボードが変わることもあるので、ターミナルに貼り付ける前に中身を確かめると安心です。
tmux を使っているとき
tmux はそれ自体がターミナルです。プログラムの出力はまず tmux に届き、tmux が外側のターミナルに画面を描き直します。知っている OSC は自分で処理し、知らないものは捨てます。そのため tmux の中で通知を送っても、外側のターミナルには届きません。プログラムが OSC 52 でコピーした内容も、既定では外に渡さず、tmux 自身のコピーモードでコピーしたものだけを渡します。
~/.tmux.conf に次の 2 行を加えます。
set -g set-clipboard on
set -g allow-passthrough on
そのあと tmux source-file ~/.tmux.conf を実行するか、tmux を開き直します。1 行目で、プログラムがコピーした内容が tmux を通り抜けて手元に届くようになります。2 行目(tmux 3.3 以降)は、tmux が知らないシーケンスをプログラムが外へ「素通し」できるようにするものです。ただし通知のコマンドは、もう一枚包んで次のように書きます。
make; printf '\ePtmux;\e\e]9;ビルド完了\a\e\\'
on はペインが見えているときだけ通します。コマンドの実行中に tmux の別のウィンドウに切り替えるなら、tmux 3.4 以降では on を all に変えます。
ShellRS では
ShellRS は macOS、Windows、Linux で使える無料のオープンソース SSH クライアントです。ここまでの話は、ShellRS では次のようになります。
- 通知:OSC 9 と OSC 777 の両方に対応しています。ShellRS が前面にないときはシステムの通知を出し、クリックするとそのターミナルに戻ります。前面にあってもターミナルが別のタブにあるときは、ウィンドウの右上に知らせが出ます。通知のタイトルはタブ名なので、どのホストから来たかがわかります。Settings › Terminal › Notifications でオフにできます。
- クリップボード:リモートのプログラムが OSC 52 でコピーした内容は、そのまま手元のクリップボードに入ります。リモートのプログラムが手元のクリップボードを読むことはできません。
- リンク:プログラムが示したリンクと、出力中の
http://、https://のアドレスは、下線つきの青で表示されます。⌘(Windows と Linux では Ctrl)を押しながらクリックするとブラウザーで開きます。リンクの上にポインターをしばらく置くと、実際のアドレスが表示されます。 - カレントディレクトリ:右サイドバーの Files は、OSC 7 を使ってターミナルのカレントディレクトリに追従します。bash や zsh がまだ設定されていなければ、ShellRS が自動で設定するか尋ねます。
- タイトルと配色:ローカルターミナルのタブには、シェルやプログラムが設定したタイトルが表示されます。vim などがターミナルの背景色を尋ねると、現在のテーマの色が返ります。
詳しくは ShellRS のマニュアルを参照してください。ShellRS はダウンロードページから入手できます。