2011/04/03

ssh先screenのペーストバッファをクリップボードに貼り付ける

GNU Screen の数ある魅力のひとつとして、端末に表示されている文字列をマウスによる操作を必要とせずにコピー/ペーストできる、ということが挙げられます。同じようなコマンドを別のウィンドウで実行したいときや、文字列の一部を再利用したいときなどに大変便利です。しかし当然ながら、コピーした文字列は screen プロセスのメモリ領域(ペーストバッファ)に格納さるため、ssh 接続した先で実行している screen のペーストバッファを手元に欲した場合、やむなくマウスで端末を選択(=クリップボードにコピー)することになり、いつも歯がゆい思いをしていました。つい先日までは。

ファイルに出力できるということは、つまり繋がるということ

ペーストバッファの内容は writebuf コマンドでファイルに出力できます。その出力ファイルをクリップボードに取り込むコマンドと連携させることで手元の端末でペーストバッファを活用できます。

余談ですが、端末の矩形選択は PuTTY や TeraTerm、Terminal.app では Alt キーを押すことで、GNOME Terminal では Ctrl キーを押すことで出来ますが、Mac OS X で普段使用している iTerm2 ではどちらも出来ずに悔しい思いをしていましたが、もう過去のことです。この方法によって screen で(縦分割している特定の領域などを)矩形選択してクリップボード経由で利用できるようになり、かなり幸せになれました。

そんな小さな幸せに満足していたある日、次の記事を見かけました。

なるほどなるほど。Netcat でファイルの中身を送信して ssh のリモートフォワード経由で受信すると。その手がありましたか。この記事では Mac OS X の pbcopy コマンドを launchd でデーモン化する方法を紹介していますが、仕組みとしてはソケット通信とクリップボードへのアクセスができれば OS は問いませんね。

繋ぐための道具を用意する

長い間渇望していた事が実現できそうなことは分かりましたが、要望がシンプルすぎるためか、なかなか手頃なツールを見つけられませんでした。

  • TCP で受信したテキストデータを
  • クリップボードに書き込む
どちらもサンプルコードをつなぎ合わせるだけで出来そうなので、こういうのは覚えてみたい言語(私の場合、Python や Ruby)で実装してみるのが一番なのですが、いち早く実現したかったので Perl で書きました。 Perl はログ調査などで使い捨てコードをよく書きますが、モジュールやソケット通信、クリップボードなどの正しい使い方はあまり理解していません。自身の英語習熟度の問題で README として記述できなかったので、以下に使い方をまとめます。

ClipboardTextListenerについて

このツールは以下の機能を提供します。

  • 任意のポートを TCP で Listen し、
  • 受信したデータを(テキストと見なして)クリップボードに書き込みます。
    • 可能であれば Win32::Clipboard を利用します。
    • それ以外は OS コマンドを利用します。
      • Mac OS X: pbcopy
      • Linux: xsel, xclip
      • Cygwin: putclip
      • Windows: clip.exe

次のような設定を起動時に指定できます。

オプション初期値説明
-addr addresslocalhostリスニングアドレス
-port port52224リスニングポート
-encoding encodingshiftjis出力エンコーディング
-key accept_keychange_on_install事前共有キー
-verbose level_num0端末出力レベル
  • 0: 起動時のメッセージ以外は出力しない
  • 1: 受信時に日時を出力
  • 2: それに加えてデータ内容を出力

基本的に localhost で待ち受けることを想定していますが、誰でもクリップボードに書き込めてしまうと困るので、送信データの 1行目を事前共有キーとして認識します。その文字列がツールに設定したものと一致しない場合は書き込みません。

以下に注意点や認識している点を挙げます。

  • 自分ひとりしかアクセスしない前提なのでシングルスレッドで動作します。多重アクセスしないでください。
  • オプションとして指定する値の妥当性チェックはしていません。あまり変な値を設定しないでください。
  • 停止する方法は用意していません。Ctrl-C するかプロセスを kill してください。
  • 受信したデータの文字コード変換に Encode.pm を使用していますが、出力エンコーディングを shiftjis にしたままでも Windows(Cygwin、ActivePerl) はもとより、Mac OS X や Linux でもとくに化けることなく OS で使用しているコードに変換されています。この辺の理解はあいまいですが、内部表現に変換されることで上手く処理されているものだと思っています。
  • 自分がよく使う環境をはじめとして、以下の OS、Perl 実行環境で動作を確認しています。
    • Mac OS X 10.6 / MacPorts perl5.12 / pbcopy
    • Windows XP、7 / Cygwin perl5.10、ActivePerl perl5.12 / Win32::Clipboard、putclip
    • Ubuntu 10.10 / perl 5.10 / xsel、xclip

ssh先screenのペーストバッファをクリップボードに貼り付ける方法

前置きが長くなりましたが、ようやく本題です。ツールを実行する端末と GNU Screen を実行している端末でそれぞれ設定が必要です。

  • ツールを実行する端末
    • ツールの設定と起動
    • sshリモートフォワードの設定
  • screenを実行している端末
    • (Netcatの導入)
    • 事前共有キーファイルを作成
    • .screenrcの設定
上から順に説明します。

ツールを実行する端末の設定

先に紹介したツールを github から取得して pl ファイルを適当な場所に置きます。実行権限の付与は任意ですが、つけてあるものとして説明します。はじめにツールの事前共有キーを変更します。これは自分だけが分かるものに変更してください。その他の設定は必要に応じて変更してください。設定の変更は直接ファイル中の値を書き換えるか、引数で任意の値を指定します。

設定の変更を終えたら、確認のため引数に -verbose 2 をつけてツールを起動します。

% ~/git/ClipboardTextListener/clipboard_text_listener.pl -verbose 2
2011/04/03 16:37:44 ClipboardTextListener[16216]: listening localhost:52224 (-verbose 2)
別のシェルから Netcat を用いて動作を確認します。まずは事前共有キーなしの場合。
% date | nc -w1 localhost 52224
2011/04/03 16:39:31 ClipboardTextListener[16216]: (127.0.0.1:50782) *** NOT ACCEPTED ***
つぎに事前共有キーありの場合。
% (echo change_on_install; echo $LANG; date) | nc -w1 localhost 52224
2011/04/03 16:45:12 ClipboardTextListener[16216]: (127.0.0.1:50918) *** RECEIVE TEXT *** 48
(Encoding: utf8 -> shiftjis)
ja_JP.UTF-8
2011年 4月 3日 日曜日 16時45分12秒 JST
ツールの出力で受信を確認できたら、別途エディタなどで送信したデータ(この場合は echo $LANG と date コマンドの出力)がクリップボードから貼り付けできることを確認します。

ここまでの動作に問題がなければ ssh クライアントの設定ファイル(デフォルトでは $HOME/.ssh/config)にリモートフォワードの設定を記述します。コマンドラインでも指定できますが面倒なのでファイルに書いておきます。ポート番号を変更している場合は適宜読み替えてください。

Host example.com
  ...
  RemoteForward 52224 localhost:52224
この設定により ssh 接続先(example.com)の 52224 ポートが localhost:52224 に転送されるようになります。ツールを実行する端末の設定は以上です。

screenを実行している端末の設定

ここからは screen を実行している端末の設定です。ファイルの転送には Netcat を使用しますが、screen を実行しているサーバに導入されていない場合は別途導入するか別のコマンドを用意してください。ネットワーク転送コマンドの準備ができたら、リモートフォワードの確認として対象サーバに ssh 接続して「事前共有キーありの場合」で実行したコマンドを実行します。

example.com % (echo change_on_install; echo $LANG; date) | nc -w1 localhost 52224
2011/04/03 16:48:45 ClipboardTextListener[16216]: (127.0.0.1:52106) *** RECEIVE TEXT *** 50
(Encoding: euc-jp -> shiftjis)
ja_JP.EUC-JP
2011ǯ  4??  3?? ?????? 16:48:42 JST
このツール出力例では日本語文字列が化けていますが、Mac OS X で Carbon Emacs の scratch バッファに貼り付けたデータは次のように変換されています。
ja_JP.EUC-JP
2011年  4月  3日 日曜日 16:48:42 JST

つぎに事前共有キーを記述したファイルを用意します。サーバ上の他人が読めないように適切な場所、パーミッションを設定してください。

example.com % echo change_on_install > ~/.exchange.key
example.com % chmod 0600 !$
chmod 0600 ~/.exchange.key
最後に .screenrc の設定です。ここまでコマンドラインで実行していたことを GNU Screen で行えるようにします。折り返して表示されているかもしれませんが、実際は bufferfilebind で始まる 2行です。
bufferfile 'exchange-file'
bind ^] eval 'writebuf' 'exec sh -c "cat .exchange.key exchange-file | nc -w1 localhost 52224"' 'echo "paste to remote"'
  • ペーストバッファの入出力ファイルを(screen を起動したディレクトリ配下の)exchange-file と定義
  • Ctrl-] をリモート貼り付けコマンドとして次の処理を実行するよう定義
    1. ペーストバッファの内容を bufferfile で指定したファイルに出力(writebuf)
    2. 事前共有キーファイルと bufferfile を cat して nc するコマンドを実行(exec)
    3. 実行したことが分かるようにステータスラインにメッセージを出力(echo)
事前共有キーファイルや bufferfile の場所、Netcat で使用するポート番号などは使用する環境に合わせて変更してください。設定の変更を反映させるため source コマンドで .screenrc を再読み込みさせるか、screen を再起動してください。

長くなりましたが、ここまでの設定を終えると ssh 接続先 screen のペーストバッファを Ctrl-a Ctrl-] で nc コマンドと ssh トンネルを通じてクリップボードに貼り付け出来るようになります。

おわりに

.screenrc に定義するリモート貼り付けコマンドは、あえてデフォルトのペーストバッファ貼り付けコマンド ] に類似した Ctrl-] に割り当てています。これによりローカル、リモートをそれほど意識することなく GNU Screen のペーストバッファを利用できるようになりました。そしてツールの動作を通じて、久しぶりにパイプと ssh 転送の便利さを実感しました。

私は Mac OS X、Windows、Linux 問わずローカルでも GNU Screen を利用していますので、今回用意したツールを backtick コマンドとして実行しています。とはいえ、このためだけにツール実行環境を用意するのが難しい場面もあるかもしれません。その場合の代案は今のところありません。とりあえず私の環境ではこうすることで出来ましたよということで参考情報として受け取っていただければ幸いです。

いまのところそこまでの欲求がないので試していませんが、この考え方を screen の zmodem などと組み合わせれば、ssh 先 screen で操作しているサーバ上の任意のバイナリもローカルに転送できるはずです。以下の記事は大変参考になります。

まだまだ GNU Screen は活用できそうです。

2011/04/24追記

先に紹介したツールを使用していると、クリップボードへの書き込みが完了するよりも前に OS 上のペーストコマンドを実行してしまい悲しい思いをすることが多くなりました。それからは backtick で動作させるのではなく、ひとつのウィンドウで動かしてその画面出力を見てコピーの完了を確認するという面倒なことをしていました。それもどうかなぁと思っていたところ、以下の記事を見かけました。

この記事にヒントを得て、自分が使うであろう OS で通知するようにしてみました。下の図はツールで通知している様子を並べたものです。順に Growl(Mac OS X)、NotifyOSD(Ubuntu)、通知領域(Windows XP、7) を使用しています。これでだいぶ使えるようになりました。ちなみに画面出力、並びに通知される文字列はクリップボードにコピーした文字列の先頭部分です。この例では全て同じ文字列にしていますが、コピーが完了するたびにこれが表示されるわけではありません:p

クリップボードへのコピー完了後に表示される通知の例

0 件のコメント:

コメントを投稿