接続済み SSH セッションからファイルをダウンロードする
別のコンピュータから手元のコンピュータへファイルを転送するというニーズはいつの時代でもあるもので、それは現代においても同じです。しかしどんなに文明が進んだとしても、そう簡単には解決しない問題も同時にあり続けます。
通常、SSH で接続できるコンピュータであれば、scp を使えば特に何も考えず、鼻クソをほじりながら、あるいは便器に座りケツニューロリンカーをバーストリンクしている状態であっても、ファイルを転送することは簡単にできます。
しかし、非常に限定されたケースであるとはいえ、踏み台にするサーバやシステム構成上、すんなりと SSH 接続を確立できないホストがあることも事実です。そこに接続してシェルを操作することはできる、でもファイルを手元に持ってくることが難しい。そんなジレンマが僕らの前に立ちはだかるわけです。
この問題について、定期的に悩んでは「そんなに量もないし、コピペして繋ぎあわせよう」だとか「既に立ってる HTTPD でプロキシ経由でとってこよう」とか、なかなかスマートとは言いがたい解決をしてきましたが、この度ようやく解決できそうだというところまできたので書き記すことにします。
ttyrec -u を使う
https://twitter.com/kzys/status/220113051536461824 で教えてもらったのですが、ttyrec には uuencode された文字列を検出して手元に書きこみ機能があり、ttyrec -u で新しいシェルを起動して、その中で SSH 接続を確立し、uuencode を実行することでファイルを転送することができます。
これは事前に ttyrec 経由で SSH のプロセスを立ち上げる必要があるので「接続済み SSH セッションからファイルをダウンロードする」方法とは言えませんが、何らかの理由があって簡単に接続を確立できないホストなら、手軽にファイルを転送できて便利そうです。
screen を使う
ttyrec -u でもいいのですが、やはり「接続済み SSH セッションからファイルをダウンロードする」方法が欲しいと思い、screen の log 機能を使って ttyrec -u 相当のこと (すなわち uuencode された文字列を検出して、手元に書きこみ) をしようと思いました。普段から手元で screen を起動しているなら、それを使えばいいだけでした。
$ uutransfer 1
のように起動します。引数は screen におけるウィンドウ番号です。ここではウィンドウ番号 1 に接続済みの SSH セッションがあるという前提です。
起動すると
Waiting for log... Watching window 1
とメッセージが起動したウィンドウに出ると同時に、ウィンドウが 1 に切り替わります (つまりメッセージは見えません)。ウィンドウ1はログを保存するモードになるので、ウィンドウステータスに (L) がつきます。
この状態で、ウィンドウ1 で
$ uuencode foo.txt foo.txt
とすると、手元に foo.txt がダウンロードされ、screen に '/Users/cho45/foo.txt was created. md5: bd5ce86cd3e2473bf32bcb5e2a824453' といった感じのメッセージが echo されます。
起動したウィンドウに戻るとこんな感じのメッセージがでています。
foo.txt: Detect uuencoded file... foo.txt: Writing... 803 bytes foo.txt: Done /Users/cho45/foo.txt 580 bytes bd5ce86cd3e2473bf32bcb5e2a824453
Ctrl-C で止めるまで動き続けるので、終わったら Ctrl-C をして止めます。止めると、screen 側のログを保存するモードも終了します。
おまけ
uuencode が意外にシステムに入っていなくてダルい
ruby -e 'puts "begin 666 #{File.basename(ARGV[0])}\n#{[File.read(ARGV[0])].pack("u")}`\nend"' fileperl -MPath::Class -e 'my $file=file(shift);printf("begin 666 %s\n%s`\nend\n",$file->basename,pack("u",scalar $file->slurp))' file