📎

OSC 52 で出力をクリップボードにコピーするためのワンライナー

2024/12/21に公開

TL;DR

以下のコマンドで ./cmd の出力をクリップボードにコピーできる。

$ ./cmd | base64 | xargs -I{} echo -n $'\x1b]52;;{}\x1b\\'

OSC 52 とは?

OSC は Operating System Command の略です。
Operating System Command は何かというと、ESC ] ... ESC \ の並びのエスケープシーケンスのことです (ESC0x1b の 1 byte)。
ESC ] の直後に数字が続くパターンが多く、特に ESC ] 52 を先頭にしたエスケープシーケンスを OSC 52 といいます。

それで、この OSC 52 が何をしてくれるのかというと、テキストをクリップボードにコピーすることができます。
具体的な書式は以下のようになっています (空白は実際には入力しません)。

ESC ] 52;<Pc>;<Pd> ESC \

c.f. XTerm Control Sequences: Operating System Commands

<Pc> はクリップボードの種類を表わす文字列です。空だと s0 と同じ意味になるようです。詳しい説明は XTerm Control Sequences のページを参考にしてください。
<Pd> はクリップボードにコピーしたいデータを base64 エンコーディングした文字列です。

一つ例を試してみましょう。
foo という文字列をクリップボードにコピーしたいとします。まずは base64 エンコーディングしてみます。

$ echo foo | base64
Zm9v

base64 エンコードした結果 Zm9V という文字列になることがわかりました。
これを OSC 52 のエスケープシーケンスに埋めこんで実行します。

$ echo -n -e "\e]52;;Zm9v\e\\"

この状態で Ctrl + V などでペーストしてみると、foo という文字列が入っているのが確認できると思います。

OSC 52 は何がうれしいの?

コマンドの出力コピーするだけなら pbcopy とか clip.exe とか xsel -b とか使えばいいじゃんと思う方もいらっしゃるかもしれません。
確かにローカルの開発環境で使うだけなら以上のようなコマンドに標準出力を流せば十分です。

しかし、よく困るのが SSH でリモートに接続した際のコピーではないでしょうか?
X11 Forwarding を設定していれば xsel -b 等を用いてコピーしてくることはできるのですが、ローカルに X server を立てておかないといけないとか、リモート側に xsel コマンドを入れとかないといけないとか、そもそも X11 Forwarding が許可されていない場合があるとか、いろいろと面倒臭い部分も多いのです。

OSC 52 は端末が処理するので、SSH していようと何だろうと端末さえ OSC 52 に対応していれば簡単に使うことができるのが嬉しいポイントです。
唯一注意すべきなのは、端末が対応しているか、また対応しているとして有効化しているかという点になります。筆者が普段使っている Wezterm は特に設定しなくても OSC 52 が有効になっているようです。

tmux 用の設定

SSH 先では tmux を使う方が多いでしょう。
tmux は端末上で動いているアプリケーションの出力を一回 tmux 側で吸いとって、場合によってはいろいろと処理をしてから外側の実際の端末エミュレータに渡してくるので、ちょっと注意が必要です。
OSC 52 に関しては基本は以下を .tmux.conf に設定しておけば良いと思います。

set-option -s set-clipboard on

詳しくは以下などを参考にしてください。

https://zenn.dev/tennashi/scraps/04e226127a4b1d

https://github.com/tmux/tmux/wiki/Clipboard

ワンライナーでコピー

さて、本題のワンライナーです (bash, zsh 用です)。
以下のワンライナーで ./cmd の標準出力をクリップボードにコピーできます。

$ ./cmd | base64 | xargs -I{} echo -n $'\x1b]52;;{}\x1b\\'

まず、OSC 52 のデータ部分は base64 エンコードしたデータじゃないといけないので、最初に base64 コマンドに通します。

次に、標準入力の base64 データをエスケープシーケンスを出力する echo に埋めこみたいのでプレースホルダー (-I{} ) 付きで xargs コマンドを使います。
このときちょっと注意が必要なのが、echoxargs から起動されるのでシェル組み込みではなく外部コマンドの /bin/echo 等が呼ばれるという点です。すると、\e -> ESC 等を解釈してくれる-e オプションが使えなかったりすることがあります [1]
そこで、$'...' という bash/zsh の特殊なクオートを使っています [2]。この $'...' の中では \xHH の形式の 16進記法を対応する 1 byte に展開してくれるので今回のテクニックが成立します。

便利なシェル関数・シェルスクリプトもあるよ

正直なところ、何回も使うのであればたぶん以下のようなシェル関数やシェルスクリプトを使ったほうが良いです。

https://gist.github.com/ttdoda/6c30de9a5f2112e72486

https://github.com/libapps/libapps-mirror/blob/main/hterm/etc/osc52.sh

関数定義するのもめんどいし、シェルスクリプト持ってくんのもめんどくさいし、とにかく今 1 回だけコピーしてきたいんじゃ!ってときにサッとワンライナーで書けたら役に立つ...かもしれません。
以上

脚注
  1. 筆者の使っている Mac 環境だと使えませんでした。Linux 環境なら GNU Coreutils 版が入っていることが多いと思うのでたぶん -e オプションも使えることが多いと思います ↩︎

  2. man bash(1): QUOTING ↩︎

GitHubで編集を提案

Discussion