コマンドプロンプトなコマンドの入出力が文字化けする

前提

  • PowerShellやWindowsの内部エンコーディングはUTF16
  • 日本語WindowsだとコンソールのエンコーディングはSJISが既定になっている
  • SJISとUTF16の変換は自動で行われ、PowerShellなコマンドだけを使うなら通常は問題ない
  • しかし、コマンドプロンプトなコマンドは入出力が通常はSJISが想定されており、
  • Linux由来のコマンドは入出力は通常はUTF8(またはUS-ASCII)が想定されている
  • そのためコマンドレットと非PowerShellなコマンドを混ぜて使うとエンコーディングが違って文字化けすることがある
  • これを解決するには、(1)非PowerShellなコマンドのエンコーディングを変更するか、(2)PowerShellのエンコーディングを変更するか、どちらか
  • 非PowerShellなコマンドはエンコーディングが固定になっていることが多いので、ここでは(2)のPowerShellのエンコーディングを変更して問題を解決する
  • なお、エンコーディングは入力と出力それぞれ考慮する必要がある

PowerShellの出力エンコーディングをSJISへ変更する

 PS> Get-Date            (1)
 2020年5月6日 17:42:00
 
 PS> Get-Date | clip.exe (2)
  • (1)は化けずにそのままコンソールに出力される
  • (2)はclip.exeがUTF16に対応してないので、クリップボードには文字化けした文字列が入っている
  • (ちなみにclip.exeは文字列をクリップボードにペーストすると昔からWindowsにあるコマンド)
  • これを解決するには以下のようにする
 PS> $OutputEncoding = [Text.Encoding]::Default  (1)
 PS> Get-Date | clip.exe
  • (1)のように$outputEncodingをSJISに指定するとPowerShellの出力がSJISになるので、clip.exeが文字列を読み込めるようになる

補足

 PS> [Text.Encoding]::GetEncoding('sjis')
 BodyName          : iso-2022-jp
 EncodingName      : 日本語 (シフト JIS)
 HeaderName        : iso-2022-jp
 WebName           : shift_jis
 WindowsCodePage   : 932
 IsBrowserDisplay  : True
 IsBrowserSave     : True
 IsMailNewsDisplay : True
 IsMailNewsSave    : True
 IsSingleByte      : False
 EncoderFallback   : System.Text.InternalEncoderBestFitFallback
 DecoderFallback   : System.Text.InternalDecoderBestFitFallback
 IsReadOnly        : True
 CodePage          : 932
 

PowerShellの入力エンコーディングを変更する

 PS> $OutputEncoding = [Text.Encoding]::GetEncoding('utf-8')  (1)
 PS> gc .\1.json | jq.exe                                     (2)
 {
   "a": "日本語",
   "b": "英語"
 }
  • (1)でPowerShellの出力をUTF8にしたので、(2)でjq.exeはJSONをパースできている。ここまではOK
 PS> gc .\1.json | jq.exe | Select-String "日本語"       (1)
                                                        (2)
 PS> [Console]::OutputEncoding = [Text.Encoding]::UTF8  (3)
 PS> gc .\1.json | jq.exe | Select-String "日本語"       (4)
  "a": "日本語",                                         (5)
  • しかし、(1)のようにjq.exeの出力をPowerShellのSelect-Stringに渡すと、
  • jq.exeはUTF8を出力しているのに、Select-Stringは入力をSJIS(からUTF16に変換する)に想定しているので、(2)のようにマッチに失敗する
  • これを解決するには(3)のようにPowerShellの入力をUTF8にしてから、(4)のようにSelect-Stringに渡すと、(5)のようにマッチに成功する

補足:なんでこんな問題が起きるのか

  • Linuxのコマンドはパイプでつなげて使うが、パイプからは文字列が来る想定で設計されている
  • 一方で、PowerShellのコマンドはパイプからオブジェクトが来る想定で設計されている
  • したがって、PowerShellをPowerShellらしく使いたいなら、オブジェクトを処理できないコマンド(=Linux由来のコマンド)は使わない方がいい

参考

https://ladybug.hatenadiary.org/entry/20111203/p1


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS