Perl で作る日本語フィルタつき ssh ラッパー

Cygwin の ssh は便利なんだけど日本語が通らないのが玉に傷。というか通るけ ど EUC 表示されてもうれしくないので、なんとか sjis に変換したい。

というわけで、作ってみました jssh.pl です。 なお Jcode モジュールが必要です。cygwin には Jcode モジュールが入ってい ないけど perl -MCPAN -e shell として、出てきたプロンプトに install Jcode と入れるだけ。なんとらく。

ポイント: 入力コードは euc を指定することで、速度向上&判定ミス回避。

#!/usr/bin/perl
use Jcode;
open(CONV_IN, "-|") or exec ('ssh', @ARGV);
$| = 1;
my $jconv = new Jcode;
while(defined ($k = getc(CONV_IN)))
{
	if( $k =~ /[\x80-\xff]/ )
	{
		$k = $jconv->set($k . getc(CONV_IN),'euc')->sjis;
	}
	print $k;
}

をを変換する。一応。でもなんかおそくないですか。あと標準エラー出力もなん とかなりませんか。ということでもうちょっとがんばってみちゃいました。

ポイント: pipe+fork を使って、標準出力と標準エラー出力をいっしょにしちゃ う。それと、getc をやめて sysread を使うことで、perl の行ごとのバッファ リングを回避する。 漢字の半切れ出力を避けるために正規表現で検査、最後の半端バイトは次回に回 す。

#!/usr/bin/perl
use Jcode;
pipe(CONV_IN, SSH_OUT);
if( ($pid = fork) == 0)
{
	close CONV_IN;
	close STDERR;
	close STDOUT;
	open(STDERR, ">&SSH_OUT");
	open(STDOUT, ">&SSH_OUT");
	exec ('ssh', @ARGV);
}
close(SSH_OUT);
$| = 1;
my $jconv = new Jcode;
$kondo = $tsugi = "";
while( sysread(CONV_IN,$in,30000) )
{
	if( ord(substr($in,-1)) >= 128 )
	{ # $in の最後が漢字に相当するバイトの場合 (正規表現が重たいので)
		if( $in =~ /(^|[^\x80-\xff])([\x80-\xff][\x80-\xff])*([\x80-\xff])$/ )
		{ # $in の最後に漢字に相当するバイトが 2n+1 個ある場合、
		  # その最後の 1 つを次にまわす
			$tsugi = $3;
			$in = substr($in, 0, -1);
		}
	}
	print $jconv->set($kondo.$in, 'euc')->sjis;
	$kondo = $tsugi;
	$tsugi = "";
}
waitpid($pid, 0);

かんぺきです。関係ないけど電車の時刻の「こんど」「つぎ」ってわかりにくい よね(ってそれを真似するな)。

※ 2004/2/26 追記: cygwin 上なら cocot というのができました。こっちのほうがよっぽど便利です。