PuTTYを使ってリモートのシェル操作でWindowsのクリップボードにテキストをコピーする

まずは結論から。
PuTTYで「Ctrl-Insert」やマウスを使った方法ではなく、ShellやVimの操作でホストOSであるWindowsにコピーをする方法というのがこのエントリーのお話です。

前から欲しいなーと思っていた機能でなかなかやり方がわからなかったですが、あるパッチを発見したのでそれをちょっとだけカスタマイズして概ね満足できるレベルで利用することができるようになりました。

やりたいこと

  • vimのVisualモードで指定した範囲をWindowsクリップボードにコピーしたい
  • Screenのコピーモードで指定したテキストをWindiwsのクリップボードにコピーしたい
  • Shellで「cygwin の /dev/clipboard」みたいな事ができたらいいな
  • 仮想環境とかディストリとかcolinuxとかCygwinとかその他環境で差異なく利用したい
  • ログをコピーとかSCPでいちいちやりとりするのが面倒くさい
  • サーバー・クライアントでデーモンを起動して共有するという方法は環境構築で厳しいのでちょっと辛い

特にvimの操作でvisualmodeの時はPuTTYの操作でWindowsクリップボードに格納できないというのが常々きついなーと思っていました。

実現方法

PuTTYなどの仮想端末ソフトがリモートホストとは唯一の接点なのでPuTTYの操作でなんとかならないかな?と探したところ下記のパッチを発見。*1

puttyclip
http://www.spinellis.gr/sw/windows/puttyclip/

PuTTYのリモートプリント機能を利用してテキストをホストOSのWindowsに送るというものです。
リモートホストでリモート印刷を開始するechoコマンド(標準出力で仮想端末PuTTYにシグナルを送信)を実行し、その後に送信される標準出力のテキストがバッファされ、リモート印刷を終了するとWindowsクリップボードに格納されるという仕組みです。

パッチファイルコンパイル済みのPuTTYはこちら。
ただ現在のパッチはマルチバイトに対応していないので文字化けします。

操作方法

シェルで利用する

Diomidis Spinellisさんのサイトからwinclipというシェルスクリプトをダウンロードします。

winclipの内容

#!/bin/sh
echo -n '^[''[5i'
cat $*
echo -n '^[''[4i'

2行目がリモート印刷開始、3行目が標準入力を標準出力する、4行目がリモート印刷開始。
shellは実行権限をつけておきます。エスケープコード「\033」に気をつけてください。
文字列で「^[」としても駄目です。

参考情報

Terminal Emulator and Thin Client Software: Century Software
http://www.censoft.com/support/kb/?cat=34

利用するときは下記の2通りの方法があります。winclipはパスを通しておくと便利です。
例としてはwinclipシェルスクリプトはホームディレクトリにあるとします。

パイプを利用する

$ ls -la | ~/winclip

lsで標準出力されたテキストがWindowsクリップボードに格納されます。

ファイル名を指定する

$ ~/winclip hogehoge.txt
$ ~/winclip fugafuga.log
$ ~/winclip hogescript.pl

ファイルの中身が丸ごとWindowsクリップボードに格納されます。
※マルチバイトは文字化けする

screenのコピーモードで利用する

画面交換ファイルを活用します。

.screenrcに追記します。

bufferfile $HOME/.screen_exchange
bind ^y eval 'exec sh -c "~/winclip ~/.screen_exchange"'

オペレーションは2手順です。

  1. writebufを使ってペーストバッファの内容をファイルに書き込みます。「^t^[space」で範囲を指定してから「>」*2
  2. 「^t^y」でWindowsクリップボードにコピー
vimのビジュアルモードで利用する

画面交換ファイルっぽく一時ファイルを作成して活用します。
vimから:!やsystem関数を使ってvim内から直接標準出力を試みましたが、リモートプリンタがらみで応答がなかったり、コマンドラインで表示されるvimのメッセージが標準出力に含まれてしまったりしたので、最終的に一時ファイルを使う形で妥協しました。

.vimrcに追記します。

let g:vim_exchange_file = $HOME . '/.vim_exchange'
let g:winclip           = $HOME . '/winclip'
vnoremap <silent><Space>Y :call WinclipLine()<CR>
function! WinclipLine() range
    call writefile(getline(a:firstline, a:lastline), g:vim_exchange_file, 'b')
    "silent! execute ":'<,'>w! " g:vim_exchange_file
    silent! execute ':!' g:winclip ' ' g:vim_exchange_file
    call system("rm -f " . g:vim_exchange_file)
endfunction

" 未実装
"vnoremap <silent><Space>y y:call Winclip()<CR>
"function! Winclip()
"    " TODO:レジスタ汚染&エスケープ処理。このまま使うと危険あり。
"    let l:yank_text = @"
"    silent! execute ":!echo '" . l:yank_text ."' > " g:vim_exchange_file
"    silent! execute ':!' g:winclip ' ' g:vim_exchange_file
"    call system("rm -f " . g:vim_exchange_file)
"endfunction

テキストを選択中に「Y」で選択されている行のテキストがWindowsクリップボードに格納されます。

あと選択中のテキストのみ「y」は完全実装していないので使用しない方がよいです。
もし選択範囲を行指定ではなく選択中のテキストでコピーしたいのであればscreenで代用できます。

パッチをPuTTYの設定とダウンロード

ごった煮版(2007年8月6)にパッチをあてました。
パッチの当て方は過去のエントリーを参考にどうぞ。
以前にIMEのカーソル色切り替えとまとめてしまったので、今回の件と別の余分な機能がついてしまっています。

このパッチに関して蛭子屋さんに問い合わせしないようにお願いします。

パッチファイル

Gistに貼り付けました。

http://gist.github.com/69564/

patchをあてる時はsjisかつ改行コードはLFにする必要があります。
通常利用のリモートプリンタは使用できないようにし、デフォルトで必ずwinclipの機能を使うように変更してあります。

コンパイル済みのPuTTY

コンパイル済みのzipファイルをこちらからダウンロードできるようにしておきました。

まとめ

  • PuTTY本家をはじめ色々なパッチを作成して公開してくれた方に感謝
  • 自分のやりたいことに大方対応できた
  • マルチバイトは持ちこし
  • ちょっとLife Changing
  • Macの人はpbcopyやfakeclipを使うとよい

*1:遭遇までに2年以上かかりました

*2:escapeキーのデフォルトはCtrl-aですが、自分はCtrl-tに変更しています