clip.exeにUTF-8のテキストを渡すと文字化けするようになったので、代替ツールを作った
作成日:
問題の概要
clip.exe とは「標準入力に渡されたテキストをクリップボードに入れる」働きを持つWindows 10の標準ツールである。このツールを私はWSLのソフトウェアからの出力をWindows側で使う為に使ってきた。例えば、「WSLのEmacsでコピーしたテキストをWindowsのクリップボードにも入れる1」といった用途だ。
しかし最近になって、このclip.exeでクリップボードに入るテキストが文字化けするようになった。最初は私の環境が何か壊れたのかと思ったが、Twitterで検索したところ私以外にも起きているらしいことが分かった2。
もう少し調べてみると、clip.exeにUTF-8でテキストを渡したときに文字化けし、CP932(≒Shift-JIS)に変換してから渡すと文字化けしないことが分かった。
# 文字化けする
$ LANG=ja_JP.UTF-8 date | clip.exe
# 文字化けしない
$ LANG=ja_JP.UTF-8 date | iconv -t cp932 | clip.exe
つまり、clip.exeがCP932で(他言語版は試していないがおそらくWindowsのロケールの文字コードで)しか受け付けないようなのである。かつてはUTF-8のまま渡してそのまま問題無くクリップボードに入っていたはずなので、いつからかこのような挙動の変更が入ったようだ。時期については不明だが、2023年3月6日にそれらしき言及が見られる3。
どのような変更が入ったかについては
- clip.exe が今までUTF-8を受け取っていたが、clip.exeにWindowsのロケールで受け取るような仕様変更が入った
- 今まではWSLからパイプでテキストをWindowsプログラムに渡すときに暗黙裡にWindowsのロケールに変換されていたが、変換が行われなくなった
が考えられる。Twitterには後者であることを示唆するツイート4があったが、WSLのリリースノートを軽く眺めた限りではそれらしい変更は見当たらなかったためよく分からない。
解決策1(CP932に変換)
さて、どのような変更が入ったかは分からなくても、前述したようにclip.exeに渡す前にCP932に変換してやれば日本語テキストは文字化けせずにコピーできる。例では文字コードの変換にiconvを使っているが、nkfなどでもよいだろう。
$ echo "烏丸千歳" | iconv -t cp932 | clip.exe
この方法は標準的なツールで対応できるという利点があるが、当然ながらCP932に含まれない文字が含まれているとコピーできない。例えば、Unicode絵文字含まれるテキストなど。
$ echo "ご注文は🐇ですか?" | iconv -t cp932 | clip.exe
iconv: 位置 12 に不正な入力シーケンスがあります
解決策2(代替ツールを使う)
問題を解決するには、単純に「UTF-8のテキストをクリップボードに正しくコピーできるclip.exeの代替」があればよい。標準クリップボード形式 (Winuser.h) - Win32 apps | Microsoft Learn(クリップボードに入れられるデータの形式を示す文書)を見ると CF_UNICODETEXT
があるため、理論上は実現できそうである。
というわけで、uclip.exe を作った。使い方はREADMEに書いたとおり、uclip.exeをclip.exeの替わりに使うだけである。GitHubのリリースにビルド済みバイナリがあるので、これを適当なパスが通った場所に置いてやれば、あとは
$ echo "ご注文は🐇ですか?" | uclip.exe
でクリップボードにテキストが入る。なお、クリップボードに書き込むという性質からかセキュリティソフトに誤検知されることがあるようなので、不安であれば自分でビルドしていただければと思う(Visual Studio 2022 でslnを開いてビルドするか、GitHub Actionsの定義ファイルにあるようにmsbuild
コマンドを打てばビルドできる)。
以上、見様見真似で作った50行に満たない簡素なプログラムだが、同じ問題で困っている方のお役に立てば幸いである。