home / uni / Cygwin with Japanese Cygwin + CygTerm + PuTTYと日本語

 Cygwinで日本語を扱う際の問題点を、Subversionのdiffコマンドをlessで見る場合を例にして説明します。

●Cygwinのlocale
 locale(ロケール、英語の発音ではロカールの方が近い)はシステム全体の文字コードを管理している。 実際には LANG・LC_なんちゃら という一連の環境変数でlocaleシステムコールが制御されていて、localeコマンドでどのような設定になっているか確認できる。 詳しくはCygwin User's Guide の Internationalization を参照のこと。

 環境変数には例えば「ja_JP.utf8」のような文字列を設定する。 ピリオドの前が「言語」、後ろが「文字コード」。 この例だと、アプリケーションが対応していれば(メッセージテーブルを持っていれば)メッセージは日本語で表示され、その際の出力文字コードはUTF-8になる。 日本人が読む場合は都合がいいだろうが、シェルなどで自動化する場合はかえって迷惑な場合がある。 その場合は言語を「C」にしてしまえばよい。

 locale -a と実行すると使用できる言語・文字コードの一覧が表示されるが、日本語関係はあまり多くない気がする。 一般的にUnixでは LANG 環境変数に ja_JP.eucJP などと設定して使うものだが、日本語関係で使えそうなのは ja_JP.utf8・japanese.sjis・japanese.euc くらいである。 /usr/share/locale/locale.alias に別名定義があるのだが、japanese.sjis は ja_JP.SJIS と定義されているのに、locale -a で ja_JP.SJIS が出てこない。 不思議だ。 この他にWindowsコードページを指定することもできるようで、例えばC.CP932やらja_JP.CP932やらという指定もできる(ように見える)。

 デフォルトでは C.UTF-8 になるようなので、UTF8がいやな人はWindowsの環境設定でLANG環境変数を定義してしまうとよい。 こうすればWindowsのコマンドプロンプト(通称DOS窓)からCygwinのコマンドを実行する場合でもこの設定が使われる。

●iconv
 iconvは文字コードを変換してくれるツール。 libiconvかlibiconv2あたりをインストールすると付いてくる(たぶん)。

iconv -f 変換元文字コード -t 変換先文字コード ファイル名

の形で使う。 -f はfrom-codeの略で、-t はto-codeの略。 文字コードは iconv -l で一覧が表示される。 とりあえずよく使うのはCP932・SJIS・EUCJP・UTF8あたり。 ファイル名を省略するとフィルタになる。

page top
●円・バックスラッシュとチルダ (02 Dec 2015)
 文字コード変換で問題を起こしやすい代表選手。 Cygwin上のiconv (GNU libiconv 1.14) で変換すると以下のようになる。
fromto備考
SJISCP932EUCJPUTF8
U+005C×0x5C0x5C0x5C半角バックスラッシュ
U+00A50x5C×0x5C0xC2 0xA5半角円記号
U+007E×0x7E0x7E0x7E半角チルダ
U+203e0x7E×0x7E0xE2 0x80 0xBeオーバーライン

 ちなみに、EUCの0x5Cと0x7Eは、それぞれU+005C・U+007Eと等価と扱われるらしい。 たとえばEUCで書かれたコードがSubversionリポジトリに登録されていて、シフトJIS系の環境でdiffを取って表示する場合を考える。 Cのソースには\や~が普通に含まれているので、文字コードとしてSJISを指定すると軒並み引っかかることになる。 CP932を使う必要がある。

●PuTTYと日本語

 PuTTYはUTF-8で日本語を扱える。 シフトJISはUse Font Encodingを使うか、CP932を指定すれば扱える。 しかし、純正版はEUCが扱えない。 パッチを当てたものもあるが、ここでは純正版でどうにかしてみる。

●Subversionと日本語

 管理下にあるファイルの文字コードは特に規定されておらず、変換もされない。 変換したい場合は自分でnkfなり、iconvなりで変換する。 ただ、Subversion自身が生成するメッセージはlocaleの影響を受ける。 Unixとその仲間たちのSubversionではEUCやUTF-8が表示できたりする(こともある)。

unix$ LANG=ja_JP.eucJP svn 2>&1 | iconv -f eucjp -t utf-8
使用方法を知りたいときは 'svn help' と打ってください。

LANGがja_JP.utf8で、表示するファイルがEUCだったりすると、ヘッダがUTF-8で、ファイルの中身はEUCという、困ったデータを吐き出す。 iconvが必要なときはLANG=C svn ...とするのが吉。

 Windows版のクライアントではシフトJISになる。 UTF-8と言っても無視されてシフトJISになる。

Cygwin$ export LANG=ja_JP.utf8
Cygwin$ which svn
/cygdrive/c/program files/sliksvn/bin/svn
Cygwin$ svn 2>&1 | iconv -f sjis
使用方法を知りたいときは 'svn help' と打ってください。

 CygwinのSubversionでは日本語は表示されない。

Cygwin$ which svn
/usr/bin/svn
Cygwin$ LANG=ja_JP.utf8 svn
Type 'svn help' for usage.
Cygwin$ LANG=ja_JP.eucjp svn
Type 'svn help' for usage.
Cygwin$ LANG=ja_JP.sjis svn
Type 'svn help' for usage.

 ついでにいうと、$Date$ の展開もlocaleの影響を受ける(多分LC_TIME)。 具体的には、localeでja_JPが指定されていると、localeの文字コードに従って曜日を日月火水木金土に展開してくれる。 LANG=ja_JP.utf8で文字コードがEUCのファイルをアップデートすると、日付の部分がUTF-8になってしまって化ける。 なので、$Date$ は安心して使えない。 $Id$ ならば曜日が入らないので大丈夫。 ユーザー名が日本語の場合はシリマセン。

page top
●sedと日本語

 今回はあまり関係ないが、試しに、

$ sed -e '/A/ p' -e 'd'

とやると、LANG=Cの場合には「、」(SJIS文字コード0x8141)が引っかかるのに対し、LANG=japanese.sjisの場合には引っかからなくなるので、localeが影響しているようだ。

●lessと日本語

 Cygwin純正のlessはUTF-8にしないと日本語をまともに扱えない。 入力の文字コードと端末の文字コードが合っていれば、とりあえず less -r で表示はできるのだが、バイト列と文字数の対応関係を考慮してくれなくなるので、1行が長かったりすると、スクロール時にすぐに画面が崩れる。 UTF-8ならだいたい動く。 lessが考えている文字幅と、実際の文字幅が異なるとちょっと崩れる(「☆」とかね)。

 jlessなど、日本語に対応しているlessを入れる方法もあるが、ここではやはり純正でどうにかしてみる。

page top
●とりあえずls

 lessがUTF-8でしか使えないので、全体をUTF-8にする必要がある。 そうするとEUCやSJISのファイルが表示できないので、iconvで変換することになる。 なので、まず、PuTTYの設定をUTF-8にしたものを用意し、CygTermを実行するときに -v LANG=ja_JP.utf8 というオプションを付ける。 これでlocaleがUTF-8になる。 EUCやSJISのファイルを見たい場合は、iconv -f eucjp filename やら、iconv -f sjis filename やらすればよい。 Windows上では圧倒的にSJISのファイルが多いだろうから、LESSOPEN環境変数を使うのもよいだろう(やったことないけど)。

 で、とりあえずls。

$ echo $LANG
ja_JP.utf8

$ ls -l c:/windows
-rw-r--r-- 1 you ■■   26582 2004-08-05 21:00 グリーン ストーン.bmp
-rw-r--r-- 1 you ■■    9522 2004-08-05 21:00 サポテック織り.bmp
-rw-r--r-- 1 you ■■   65832 2004-08-05 21:00 サンタフェ.bmp
-rw-r--r-- 1 you ■■   17362 2004-08-05 21:00 しゃくなげ.bmp
-rw-r--r-- 1 you ■■   65978 2004-08-05 21:00 シャボン.bmp
-rw-r--r-- 1 you ■■   17336 2004-08-05 21:00 フィッシング.bmp

 もうこの時点でグループ名が化けてる。 lsはsjisで動かして、iconvでUTF-8にしないといけないようだ。

$ LANG=japanese.sjis ls -l c:/windows | iconv -f sjis

-rw-r--r-- 1 you なし   26582 2004-08-05 21:00 グリーン ストーン.bmp
-rw-r--r-- 1 you なし    9522 2004-08-05 21:00 サポテック織り.bmp
-rw-r--r-- 1 you なし   65832 2004-08-05 21:00 サンタフェ.bmp
-rw-r--r-- 1 you なし   17362 2004-08-05 21:00 しゃくなげ.bmp
-rw-r--r-- 1 you なし   65978 2004-08-05 21:00 シャボン.bmp
-rw-r--r-- 1 you なし   17336 2004-08-05 21:00 フィッシング.bmp

 このように書くと、lsだけLANG環境変数がjapanese.sjisになり、lsはシフトJISで出力するので、iconvで変換してやる。 いちいちこれを打つのは面倒なので、aliasにしたいところだが、そうするとlsに指定したつもりのオプションが一番後ろに行ってしまい、iconvのオプションになってしまって具合が悪い。

$ alias ls="LANG=japanese.sjis ls | iconv -f sjis"

$ ls -l
Usage: iconv [-c] [-s] [-f fromcode] [-t tocode] [file ...]
or:    iconv -l
Try `iconv --help' for more information.

 こういうときはシェル関数で。

$ unalias ls
$ ls () { LANG=japanese.sjis /bin/ls $* | iconv -f sjis; }
$ ls -l

-rw-r--r-- 1 you なし   26582 2004-08-05 21:00 グリーン ストーン.bmp
-rw-r--r-- 1 you なし    9522 2004-08-05 21:00 サポテック織り.bmp
-rw-r--r-- 1 you なし   65832 2004-08-05 21:00 サンタフェ.bmp
-rw-r--r-- 1 you なし   17362 2004-08-05 21:00 しゃくなげ.bmp
-rw-r--r-- 1 you なし   65978 2004-08-05 21:00 シャボン.bmp
-rw-r--r-- 1 you なし   17336 2004-08-05 21:00 フィッシング.bmp

 うっかり /bin/ls を単に ls と書くと、再帰呼び出しになってハマるので注意。 やってもうた時はCtrl+Zで止めて、psしてkillすべし。 文字化けはこれで直るが、パイプを通してしまうのでカラー表示ができなくなる。 シェル関数に入ったときに標準出力が端末につながっているかどうかを調べて、端末につながっていれば強制的にカラー表示を設定してみる。 ついでに、LANGがシフトJISの場合はiconvを通さずにcatで直接出力してみる。 そんなんで、.bashrc にこんなのが書いてある。

ls ()
{
    local LSFLAGS;
    if [ -t 1 ]; then
        LSFLAGS+=" --color -C";
    fi;
    LANG=japanese.sjis /bin/ls $LSFLAGS -F --show-control-char $* | case "$LANG" in
        *.[Ss][Jj][Ii][Ss] | [Ss][Jj][Ii][Ss] | "")
            cat
        ;;
        *)
            iconv -f sjis --byte-subst="<0x%x>"
        ;;
    esac
}

/bin/lsの行にcaseまで入っているのは改行を忘れたわけではなく、lsの出力をcaseコマンドに渡すため。 caseが受けた出力はさらにcaseの中のコマンドに渡される。 Unixシェル恐るべし。 --show-control-char は常用しているシェル関数に入っているのでそのまま載せたけど、LANGがきちんと指定してあればいらないっぽい。

page top
●less
 lessは次のいずれかの対応となる。

-rオプションを使う

 最も安直な方法。 lessは画面上での行の長さの管理を行っている。 「知らない文字」=「文字幅が分からない文字」が入っていると行の長さが分からなくってしまうため、文字コードを表示するようになっている。 -rオプションを付けると「知らない文字」もそのまま表示するようになる。 表示するデータの文字コードと、端末が表示できる文字コードが合っていれば、そのまま読めるようになる。

 例えば、PuTTYをCP932(Windows日本語)で使用していれば、シフトJISのファイルは-rオプションを付けるだけで読めるようになる。 シフトJIS以外のファイルは nkf や iconv でシフトJISに変換すればよい。 例えば、EUCのファイルを読みたい場合は、iconv -f euc-jp -t sjis filename | less -r とすればよい。

 この方法ではlessが行の長さの管理をあきらめるため、画面上で一行に納まらない行があるとスクロールしたときにぐっちゃぐちゃになる。

 -r の親戚に -R というのもあるが、こちらはANSIエスケープシーケンスを解釈するようになる。 ANSIエスケープシーケンスは正しく表示する(文字色などを変更できる)ようになるが、-r までの効果はない。 つまり、日本語が表示できるようになるわけではない。

別途日本語対応のjlessなどを入れる

 文字コードも自動で判別してくれると思うので、一番楽と言えば楽。

UTF-8に変換する

 LANG環境変数が正しく設定されているならば、iconv -f euc-jp filename | less などとすればよい。 lessにオプションはいらないか、ANSIエスケープシーケンスが混ざっている場合は -R を付ける。 -r を指定する場合と異なり、行の長さの管理が行われるので、ページスクロールなども正しく動く。 ただし、lessが考えている文字幅と実際のフォントの文字幅が異なるものがあり(記号に多い)、そのような場合は文字の左半分だけが表示される。

page top
●svn diff
 Cygwinに限らず、全体をUTF-8にしているので、svn diffなどのヘッダがEUCやシフトJISになっていて化けることがある。 というか、Cygwinのsvnは日本語が表示されないので、Cygwin以外のsvnでしか起きないのだが・・・。 例えば、CollabNetのWin32バイナリやSlikSvnなどでsvn diffを実行すると、たとえファイルの中身がASCIIやUTF-8であったとしても、ヘッダはシフトJISで表示されるため、この部分だけ化ける。
C:> svn diff
Index: rgbcurve.cpp
===================================================================
--- rgbcurve.cpp        (リビジョン 260)
+++ rgbcurve.cpp        (作業コピー)
@@ -1,5 +1,6 @@
 // $Id: cyg-svn-diff.htm,v 1.7 2015/12/03 15:18:07 you Exp $

+#define NOMINMAX
 #include <io.h>
 #include <fcntl.h>

 化けないようにするためにはLANG=Cにしてしまえばよい。

$ LANG=C svn diff
Index: rgbcurve.cpp
===================================================================
--- rgbcurve.cpp        (revision 260)
+++ rgbcurve.cpp        (working copy)
@@ -1,5 +1,6 @@
 // $Id: cyg-svn-diff.htm,v 1.7 2015/12/03 15:18:07 you Exp $

+#define NOMINMAX
 #include <io.h>
 #include <fcntl.h>

ファイルの中身は変換されることなくそのまま表示されるので、iconvで変換してやる。 これはCygwinに限ったことではなく、Unixとその仲間たちのsvnでも同じである。 ヘッダとファイルの内容で文字コードが異なるとiconvでうまく変換できないので、LANG=Cで実行する。 ファイル名に日本語が混ざってた場合は知らん。

$ echo $LANG
ja_JP.utf8
$ LANG=C svn diff | iconv -f eucjp
Index: euc.txt
===================================================================
--- euc.txt     (revision 2)
+++ euc.txt     (working copy)
@@ -1 +1,2 @@
 このファイルはEUCです。
+修正してみました。

 これをいちいち打つのは面倒なので、やはりシェル関数にしておくとよい。 iconvには-tを指定せず、変換先の文字コードはLANG任せにする。 と、-fには何を指定するのか? という問題が起きるが、これは適当な環境変数・・・例えばSVN_LANGに指定することにすると、例えば以下のようなスクリプトを書けばよい。

svn-diff ()
{
    local ICONV=cat
    if [ "$SVN_LANG" != "" ]; then ICONV="iconv --byte-subst='<0x%x>' -f $SVN_LANG"; fi
    LANG=C svn diff "$@" | $ICONV
}

 SVN_LANGが空ならばcatが実行されるので、svnの出力がそのまま表示される。 SVN_LANGに何か指定すればiconvを通して出力される。 シェル関数なのでSVN_LANGはexportしなくてもよい。 もちろんexportしてもいいし、一時的に指定してもよい。

$ SVN_LANG=eucjp svn-diff
Index: euc.txt
===================================================================
--- euc.txt     (revision 2)
+++ euc.txt     (working copy)
@@ -1 +1,2 @@
 このファイルはEUCです。
+修正してみました。
page top
●おまけ
 こんなsedスクリプトを作って~/.diff.sedあたりに置きます。
/^Index: / {
	s/^/ESC[1;44m/
	s/$/ESC[m/
}
/^=/ {
	s/^/ESC[1m/
	s/$/ESC[m/
	N
	N
}
/^@/ {
	s/^/ESC[1;4;33m/
	s/ @@/ @@ESC[mESC[4;37m/
	s/$/ESC[m/
}
/^+/ {
	s/^/ESC[1m/
	s/$/ESC[m/
}
/^-/ {
	s/^/ESC[36m/
	s/$/ESC[m/
}
ESCがいっぱい出てきますが、入力の仕方はお手持ちのエディタに聞いてください。 MIFESはCtrl+Vに続いて制御コード(この場合はCtrl+[を叩く)だったので、milike.keyを適用した秀丸もCtrl+Vで制御コード入力になります。 秀丸ではメニューの「その他」→「制御コード入力」でも入力できます。 viは入力中にやはりCtrl+VCtrl+[です。

 シェル関数もこんな風に書きます。

svn-diff ()
{
    local ICONV=cat
    if [ "$SVN_LANG" != "" ]; then ICONV="iconv --byte-subst='<0x%x>' -f $SVN_LANG"; fi
    LANG=C svn diff "$@" | $ICONV | sed -f ~/.diff.sed
}

すると、結果はこうなります。ターミナル風に。

$ SVN_LANG=eucjp svn-diff
Index: euc.txt
===================================================================
--- euc.txt     (revision 3)
+++ euc.txt     (working copy)
@@ -1,2 +1,3 @@
 このファイルはEUCです。
-修正してみました。
+ここは前に修正した行です。
+追加してみました。

 ターミナルとは思えない見やすさ。 ANSIのエスケープシーケンスなので、lessに -R を指定すれば、色付きのまま見ることができます。 このsedスクリプトはsvn専用というわけではなく、cvs diff -uや単なるdiff -uにも使えます。

 もっとも、Cygwinを使っているということは、秀丸も使えるということで、秀丸を使えるならdiffの結果を秀丸に出して強調表示を設定すればいいので、Cygwinでこのsedスクリプトを使うことはまずないのですが。 Unixとその仲間たちを使っているときには重宝しています。


Copyright (C) 2011-2015 akamoz.jp

$Id: cyg-svn-diff.htm,v 1.7 2015/12/03 15:18:07 you Exp $