PowerShellで外部コマンドの出力が文字化けする場合の対処法
ASCII.jp / 2023年6月11日 10時0分
PowerShellで文字化けが生じる場合がある
直接実行すると問題なく表示されるのに、PowerShellの変数に格納する、あるいはファイルに出力すると文字化けする外部コマンドがある。
具体的には、wsl.exeやwinget.exeなどだ。文字化けするのは、PowerShellの変数への格納や、cmd.exeでファイルにリダイレクトしたあと、変数やファイルを表示したときである。
先に結論から言えば、wsl.exeとwinget.exeの場合、標準出力にはバイトマークなしのUnicode(UTF-16LE)エンコードされた文字列が出力されているため、そのままでは文字化けしてしまう。
画面に出力したときに文字化けしない理由は、標準出力とは異なる方法で画面表示しているからだと思われる。ソースコードを調べたわけではないが、たとえば、コンソールAPIなどを使って画面出力しているのではないかと思われる。このときの表示はコンソールや文字エンコード設定に関わらず、正しくなる。
具体的に「wsl.exe -l」の場合で見てみることにする。まずcmd.exeで
wsl.exe -l >wsl-cmd01.txt
と出力した場合、ファイルにはバイトオーダーマークがない。このためにmore.exeなどで表示すると、文字化けしてまう。
cmd.exeには、バイトオーダーマークなしのUnicodeを読み込む機能はないが、PowerShell Ver.7または、Windows PowerShell Ver.5.1(以下両方をPowerShellと表記する)では、
Get-Content .\wsl-cmd01.txt -Encoding Unicode
とすることで、正しくファイルを読むことは可能だ。
PowerShellの場合は、話はもう少し複雑になる。PowerShellで外部実行ファイル(外部コマンド)の出力は、シフトJIS(日本語版Windowsの場合)であることを想定していて、コマンドの出力に対してUnicode(UTF-16LE)への文字エンコード変換をする。
本来Unicodeなのに、それをシフトJISだと思って文字エンコード変換するため、文字エンコードが正しくないものになり、その結果文字化けしてしまう。cmd.exeの場合とは、少し事情が違う。
PowerShellでは、外部コマンドの出力を変換して受け取るため、これをリダイレクトしてファイルに書き込んでも、誤った変換がされたファイルになる。なので、cmd.exeのようにリダイレクトしたファイルをUnicodeエンコードで読んでも正しい結果は得られない。
PowerShellで文字エンコードを正しく処理させる
PowerShellで、Unicode出力を正しく受け取るためには、コンソールの出力エンコードをUnicodeにする。これで、PowerShellは外部コマンドの出力がUnicodeエンコードであることを理解し、変換しなくなる。
そのためには、一時的にコンソールの出力エンコードをUnicodeにして、コマンドの実行後に元に戻す。具体的には、
$TempMyOutputEncode=[System.Console]::OutputEncoding [System.Console]::OutputEncoding=[System.Text.Encoding]::Unicode $x=wsl.exe -l [System.Console]::OutputEncoding=$TempMyOutputEncode
とする。
最初の行は、現在の出力エンコードを変数($TempMyOutputEncode)に保存するもの。
2行目は、[System.Console]::OutputEncodingを、Unicodeエンコード(Encodingオブジェクト)に変更している。ユニコードのEncodingは、最初からプロパティとして定義されているので、「[System.Text.Encoding]::Unicode」と指定できる。なお、シフトJISエンコードを使う場合には、以下のコマンドでエンコードを得ることができる。
[System.Text.Encoding]::GetEncoding('shift_jis')
このGetEncodingで利用できるエンコード名については、以下のページに記述がある。これはPowerShell Ver.7.x用だが、Windows PowerShell用もページの中身はほぼ同じものである。
●Encoding クラス (System.Text) PowerShell 7.x https://learn.microsoft.com/ja-jp/dotnet/api/system.text.encoding?view=net-7.0#list-of-encodings
3行目がwsl.exeの実行である。ここで、標準出力にUnicodeを出力するコマンドを実行すればよい。
4行目では、1行目で変数$TempMyOutputEncodeに保存したエンコードを使って出力エンコードを復元している。
外部コマンドへの出力エンコードを変更する
PowerShellコマンド出力を外部コマンドにパイプで渡したとき、文字化けが発生することがある。このような場合には、システム変数$OutputEncodingに定義されているエンコードが、外部コマンドが想定しているエンコードと異なっている。
たとえば、シフトJISのみ受け付けるといったように、特定の文字エンコードを想定した作りになっているからだ。Windowsの標準コマンドだと、文字列検索のfindstr.exeは、入力文字列のエンコードはシフトJISになっている必要がある。このとき、$OutputEncodingを「shift-jis」エンコードに変更する。
$OutputEncodingは、Windows PowerShellでは、「US-ASCII」(ASC383IIコード)、PowerShell Ver.7では、「UTF-8」になっている。つまり、findstr.exeに渡される検索文字列がUTF-8あるいはUS-ASCIIであるため、findstr.exeは、文字列を見つけることができない。
wsl.exeとwinget.exeの場合
今回、文字化けするコマンドの例としてwsl.exeとwinget.exeを挙げたが、現実問題としては、どちらのコマンドも他の方法によりPowerShell内で情報を正しく扱える。
wsl.exeの-lオプションと同等の情報は、レジストリから抜き出すことが可能で文字エンコードの問題はない。これについては、過去記事(「ストア版WSLをアップデートまたはダウングレードする」)で扱った。
また、wingetに関しては、同等の機能を持つPowerShellモジュールの配布が開始されている。これについては、別の機会で解説の予定だ。
コマンドの出力は文字化けしないのに、リダイレクトや変数への格納で文字化けが起こるのは、文字エンコードが原因の可能性が高い。特にPowerShellでは暗黙的に文字エンコード変換がなされているため、これを理解しておかないと苦労する。
このような場合、cmd.exeで外部コマンドを実行して、ファイルにリダイレクトをし、これをバイナリエディタなどで見るとよい。cmd.exeはリダイレクト時にもエンコードのを変換などしないため、本来の出力を確認しやすい。
この記事に関連するニュース
-
Excelをノーコードで自動化しよう! パワークエリの教科書 第9回 Excelファイルからデータを取得する方法
マイナビニュース / 2024年7月8日 11時0分
-
Windowsが今更(?)開発者に優しくなろうとしている!? 「Dev Home」は開発者にとって使い物になる?
ASCII.jp / 2024年6月23日 10時0分
-
窓辺の小石 第170回 ネットの総和
マイナビニュース / 2024年6月14日 10時13分
-
Windows Subsystem for Linuxガイド 第36回 WSL2でDockerを使う その2「Dockerエンジン編」
マイナビニュース / 2024年6月10日 17時57分
-
WindowsのPHPサーバに緊急の脆弱性、確認とアップデートを
マイナビニュース / 2024年6月10日 15時23分
ランキング
-
1「ミスiD」ファイナリストのアイドル、交通事故に巻き込まれ活動休止 「悲しくて泣く……」ファンから心配の声殺到
ねとらぼ / 2024年7月8日 19時56分
-
2『アサシン クリード シャドウズ』無断で既存団体の旗をコンセプトアートに使用―すでに謝罪済み
Game*Spark / 2024年7月8日 20時30分
-
3スタバ、一部商品を価格より高く販売していた 約10年にわたりシステム設定に不備、返金へ
ITmedia NEWS / 2024年7月8日 17時3分
-
4東京の用水路にアマゾン川の生き物が大量発生だと……? “いてはいけないヤツ”の捕獲に衝撃「想像以上にヤバかった」「ホンマに罪深い」
ねとらぼ / 2024年7月8日 22時0分
-
5一度植えたら、自動で増殖&毎年収穫を目指せる野菜5種とは? 自然農のエキスパートが伝授する方法に反響
ねとらぼ / 2024年7月8日 9時0分
記事ミッション中・・・
記事にリアクションする
記事ミッション中・・・
記事にリアクションする
エラーが発生しました
ページを再読み込みして
ください