概要
外部コマンドがUTF-8で日本語(などの非ASCII文字)を出力する場合、それをPowerShell変数として受けると文字化けすることがある。その際は、一時的に[Console]::OutputEncoding
を変更する。以下のようになる。
[Console]::OutputEncoding=[Text.Encoding]::UTF8
$result = 外部コマンド
[Console]::OutputEncoding=[Console]::InputEncoding
1liner化するならこんな感じ?
$result = Invoke-Command { [Console]::OutputEncoding=[Text.Encoding]::UTF8; 外部コマンド; [Console]::OutputEncoding=[Console]::InputEncoding }
そのまま後続処理にパイプ渡しもできる。例えば出力がJSON形式なら、次のようにパースまでつなげられる。
$result = Invoke-Command { [Console]::OutputEncoding=[Text.Encoding]::UTF8; 外部コマンド; [Console]::OutputEncoding=[Console]::InputEncoding } | ConvertFrom-JSON
ちなみに筆者は外部コマンドとして、具体的には1password CLIのop item list
のJSON形式出力をConvertFrom-JSON
につなげたかった。でも文字化けが差し障ったのでこれらを調査して、最終的にこんな感じにした。
$result = Invoke-Command { [Console]::OutputEncoding=[Text.Encoding]::UTF8; op item list --format=json; [Console]::OutputEncoding=[Console]::InputEncoding } | ConvertFrom-JSON
詳細
文字化け対策の基本的な考え方は以下より。本記事タイトルも以下をちょっともじっただけ。
PowerShellで、Unicode出力を正しく受け取るためには、コンソールの出力エンコードをUnicodeにする。これで、PowerShellは外部コマンドの出力がUnicodeエンコードであることを理解し、変換しなくなる。そのためには、一時的にコンソールの出力エンコードをUnicodeにして、コマンドの実行後に元に戻す。
(ASCII.jp:PowerShellで外部コマンドの出力が文字化けする場合の対処法 (1/2))
示されているコードは以下で、上述のものより丁寧。
$TempMyOutputEncode=[System.Console]::OutputEncoding
[System.Console]::OutputEncoding=[System.Text.Encoding]::Unicode
$x=wsl.exe -l
[System.Console]::OutputEncoding=$TempMyOutputEncode
ここから変更と二つの手抜きをしているのだけど、変更点はUnicodeではなくUTF-8を受け取るため、文字コード指定を変えている。以下「Encoding クラス (System.Text) | Microsoft Learn」からの抜粋にあるように、Unicode
はUTF-16を指す。
プロパティ | 概要 |
---|---|
Unicode | リトル エンディアン バイト順を使用する UTF-16 形式のエンコーディングを取得します。 |
UTF8 | UTF-8 形式のエンコーディングを取得します。 |
手抜きとして第一に [System.Console]
や [System.Text.Encoding]
の部分は、 System.
を省略できる。
型がシステム名前空間のルートにない場合は、オブジェクト型の完全な名前を指定します。 "System" は省略できます。
(型演算子について - PowerShell | Microsoft Learn)
次に、[System.Console]::OutputEncoding
は意図的に変えていなければ、[System.Console]::InputEncoding
のはず。この着想は以下からの類推。$Output
と[System.Console]::OutputEncoding
は別物なので、この説明があるから正しいというものではないけど、とりあえずうまくいっている。
ほとんどのシナリオでは、 $OutputEncoding の値は [Console]::InputEncodingの値に合わせる必要があります。
(基本設定変数について - PowerShell | Microsoft Learn)
2番目の手抜きは、意図的に変更している可能性もあるので、しない方が確実ではある。
参考
- ASCII.jp:PowerShellで外部コマンドの出力が文字化けする場合の対処法 (1/2)
- 型演算子について - PowerShell | Microsoft Learn
- 基本設定変数について - PowerShell | Microsoft Learn
記事内では言及しなかったが、以下のページも理解のための参考にした。