2013/02/21(木)RubyでUTF-8ファイル名&外部コマンド実行
2013/02/21 16:33
Rubyの``やsystemで外部コマンド実行するときに、実行ファイル自体や引数にUTF-8文字(SJISマッピング無し)が渡せないのであれこれ悩んだ問題。
確認はWindows7 64bit & Ruby1.9.3p0(ActiveScriptRuby)
前提。WindowsはファイルシステムがNTFSならUTF-8ファイル名を命名できる。内部コードもUTF-8になっている(はず)。しかし古いAPIを触ったり、INPUT/OUTPUT周りを見るとまだShiftJIS(Windows-31J)が多い。このズレは厄介で、ハマりどころのA代表。特にRubyの場合、UNIX寄りでWindows特有処理のケアはやっぱり甘い…
閑話休題。Windows&UTF-8の扱いが比較的良くなっているRuby1.9系(Dir.globの引数に.encode('utf-8')などでUTF-8文字列を渡すとUTF-8ファイル名が取れる)でも、マジックコメント入れてUTF-8保存しただけだと外部実行が出来ない。
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*- `utf8_filename`どうにも通らない。SJISマッピング出来るファイルなら、SJISでやればいいだけの話なのだがUTF-8にあってSJISにない文字(U+2661(はーと)とか)が混ざってると動かない。
で、とりあえず色々やってみたのだが、結論から言うと綺麗に書くのは諦めた。
ちゃんと調べるべきなのだが(やってない)、どうもRubyが叩いてるAPI自体が古いんじゃないかという。
unicode - Ruby system() doesn't accept UTF-8? - Stack Overflow
なので、苦肉の策。
バッチファイルを作成して、それを実行する。
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*- open("tmp.bat", "w"){ |fp| fp.write("chcp 65001\n") fp.write("utf8_filename\n") } out = `tmp.bat`Windowsのバッチ実行ではコードページが合えば、UTF-8ファイル名も通るのでそれを利用する。引数にファイル名を指定するときも同様。\\の数とかダブルクオート囲いとか先頭に./が必要な場合は適切にすること。
うーん、汚すぎて死にそう。
[参考]
Rubyで外部コマンドを実行して結果を受け取る方法あれこれ #Ruby - Qiita
systemuは知らなかった。
[2017/05/02 追記]
やはりきれいには書けていないが、さすがにbatを吐き出すのは汚すぎるということで苦肉の策第二段。小改善してみた。今回はnyagosを使用している。
out = "" Open3.popen3("c:/********/nyagos.exe"){ |stdin, stdout, stderr, thread| stdin.puts("chcp 65001") stdin.puts("#{COMMAND} \"#{utf8_filename}\"") stdin.close out = stdout.read }nyagosでなくても良いのだが、cmd.exeやnyaos.exeだとchcp 65001したあとにUTF-8を流し込めない。
ここらへんは、chcp後に貼り付けが出来るかどうかで確認する。
Windows APIを直接叩けば済むような問題に見えるんだよなあ。
ここはまた次回。