[Powershell] UTF-8 でエンコードされた標準出力を受け取る

Pocket

2バイト目に ASCII コードがくることを想定していないコンソールアプリなど、標準出力を UTF-8 として使用するコンソールアプリは少なくない。
しかし、PowerShell は標準では UTF-8 の標準出力をそのまま読み取ることができない。
(後述するが、単純な方法はある)

そういった場合でも、.NET Framework を駆使すれば、ちゃんと文字列として取得できる。

たとえば、以下のようなバッチファイルを、 utf8out.bat などとして用意しておく。
(ちゃんと標準ストリームを使用していることを明確にするため、あえてバッチで書いた)

@echo off
powershell -Nologo -Command "$binutf8 = [Text.Encoding]::UTF8.GetBytes([char[]]@('(','(',0x3044,0x308D,0x306F,0x306B,')',')')); [Console]::OpenStandardOutput().Write($binutf8, 0, $binutf8.Count);"

これをそのまま実行すると、

((縺・m縺ッ縺ォ))

などと文字化けしてしまうことだろう。

これを、 System.Diagnostics.Process クラスを使って、標準出力をリダイレクトさせ、それを UTF-8 エンコードで取り込ませる。

$psi = New-Object Diagnostics.ProcessStartInfo
$psi.FileName = "utf8out.bat"
$psi.UseShellExecute = $false   # プロセス起動時にシェルを使わず、標準出力をリダイレクト可能にする
$psi.StandardOutputEncoding = [Text.Encoding]::UTF8 # 文字コードを指定
$psi.RedirectStandardOutput = $true
$p = [Diagnostics.Process]::Start($psi)
$s = $p.StandardOutput.ReadToEnd()
$s
$p.WaitForExit()

そうすれば、出力は以下のように文字化けせずに表示されるはずだ。

((いろはに))

これで、UTF-8 を使うアプリとの連携もばっちりだ。

ちなみに、 FileName プロパティで指定するファイルの相対パスは、 PowerShell の カレントディレクトリではなく、 .NET のカレントディレクトリ ([System.IO.Directory]::GetCurrentDirectory() / [System.IO.Directory]::SetCurrentDirectory(...) で取得/設定できる物) である点に注意。

PowerShell v3 以上であれば、以下のように もうちょっときれいに書くこともできる。

#Requires -Version 3
$p = [System.Diagnostics.Process]::Start(
    [System.Diagnostics.ProcessStartInfo]@{
        FileName = 'utf8out.bat';
        UseShellExecute = $false;
        StandardOutputEncoding = [Text.Encoding]::UTF8;
        RedirectStandardOutput = $true;
    }
);
$p.WaitForExit();
$s = $p.StandardOutput.ReadToEnd();
$p.Dispose();
$s;

…と、ここまでは 2011年頃が初稿の記事だったのだが、 2023年現在利用可能な Windows PowerShell 5.1 や PowerShell 7.2 の場合、以下のように [System.Console]::OutputEncoding に適切な文字コードを設定してやれば、キチンと受け取れる。

[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8;
.\utf8out.bat | Tee-Object -Variable rett;

ちなみに、外部コマンドの標準出力を PowerShell で受け取る場合は [System.Console]::OutputEncoding プロパティに、 PowerShell の出力を外部コマンドの標準入力に渡す場合は $OutputEncoding 自動変数に、それぞれ適切な文字コードを設定する必要がある。

それぞれの初期値は以下の通り。

Windows PowerShell 5.1以下 PowerShell 6以上
[System.Console]::OutputEncoding プロパティ システムロケールの文字コード
$OutputEncoding 自動変数 US-ASCII BOM 無し UTF-8

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください