PowerShell で日本語が化ける ── 読み・書き・受け渡しで別々に潰す

◯目次

PowerShell で日本語が化ける ── 読み・書き・受け渡しで別々に潰す

目次

Windows の PowerShell(5.1)で作業してると、日本語がとにかく化ける。ファイルを読んでも化ける、書いても化ける、他のコマンドに渡しても化ける。「文字コードを UTF-8 にすればいい」で片付くと思いきや、化ける場所ごとに対処が違って毎回ハマるので、まとめて潰しておく。

そもそもの元凶#

Windows PowerShell 5.1 のデフォルトエンコーディングは UTF-8 じゃない。OS のロケール依存で、日本語 Windows だと CP932(Shift_JIS 系) になる。一方で今どきのツール(pip・git・node、大半のテキストファイル)は UTF-8 前提。この食い違いが化けの正体。

厄介なのは、化ける場所が1つじゃないこと。「読む」「書く」「別プロセスに渡す」 の3面で、それぞれ別の地雷を踏む。順番に潰す。

Note

PowerShell 7 (pwsh) はデフォルトが UTF-8 なので、これらの多くは自然に消える。ただ Windows に最初から入ってるのは 5.1 で、業務 PC だと 7 を入れられないこともある。ここでは 5.1 前提で書く。

1. 読むとき ── Get-Content が化ける#

UTF-8 で保存したファイルを普通に読むと化ける。

Get-Content .\notes.md                  # 化ける(CP932 として解釈される)
Get-Content .\notes.md -Encoding UTF8    # OK

-Encoding UTF8 を付けるだけ。地味だけど、これを忘れて「ファイルは正しいのに表示だけ壊れる、なんで?」と悩むのが定番。

2. 書くとき ── BOM 問題#

出力側はもう一段ややこしい。Out-FileSet-Content のデフォルトは UTF-16 LE(BOM つき) で、他のツールが読めないファイルが出来上がる。

"内容" | Out-File .\out.txt                  # UTF-16 LE。node 等が読めない
"内容" | Out-File .\out.txt -Encoding utf8    # UTF-8 …だが BOM つき

-Encoding utf8 で UTF-8 にはなるけど、5.1 の utf8 は BOM つき。BOM が邪魔なツール(一部のパーサ、シェルスクリプト、設定ファイル)だとこれで壊れる。BOM なしで書きたいなら .NET を直で呼ぶのが確実。

$enc = New-Object System.Text.UTF8Encoding($false)   # $false = BOM なし
[System.IO.File]::WriteAllText("C:\path\out.txt", $text, $enc)

読むときも .NET で明示すると確実。

$text = [System.IO.File]::ReadAllText("C:\path\in.txt", [System.Text.Encoding]::UTF8)
Tip

スクリプトで大量のファイルを一括置換するときは、Get-Content | Set-Content より、この ReadAllText / WriteAllText の組み合わせの方が、元の日本語を化けさせずに済む。実際これで記事ディレクトリの一括リネームをやった。

3. 別プロセスに渡すとき ── -Body が化ける#

意外と気づかないのがこれ。Invoke-RestMethod / Invoke-WebRequest-Body に日本語混じりの JSON を渡すと、UTF-8 で送られず受け側で化ける。

# 化ける:文字列を渡すと PowerShell が勝手にエンコードして送る
Invoke-RestMethod -Uri $url -Method Post -Body $json -ContentType "application/json"

対処は、自分で UTF-8 のバイト列にしてから渡す

$bytes = [System.Text.Encoding]::UTF8.GetBytes($json)
Invoke-RestMethod -Uri $url -Method Post -Body $bytes -ContentType "application/json; charset=utf-8"

ローカルの TTS サーバに日本語を投げたら合成音声が空(無音)で返ってきて、原因がこれだった。API に届く前に化けていた。

4. bat / cmd に日本語を書かない#

最後は文字コードというより運用の話。バッチファイル(.bat / .cmd)に日本語を直書きするのはやめた方がいいchcp 65001(UTF-8 化)と日本語を混ぜると、行がぶつ切りに壊れて、コマンドが意味不明なエラーで落ちる。

対処はシンプルで、bat / ps1 は純 ASCII に保つ。日本語を出したい・使いたいなら、その部分だけ Python など別ファイルに逃がして、bat からは呼ぶだけにする。

@echo off
chcp 65001 > nul
python run.py    :: 日本語は run.py の中に閉じ込める

こうしておくと、ランチャーの bat が文字コードで自壊する事故がなくなる。

まとめ#

  • 元凶は PS5.1 のデフォルトが CP932、周りは UTF-8、という食い違い
  • 読むGet-Content -Encoding UTF8
  • 書く:BOM なしが欲しければ .NET の UTF8Encoding($false) + WriteAllText
  • 渡す-BodyUTF8.GetBytes() でバイト列にしてから
  • bat:日本語を直書きしない。純 ASCII に保って日本語は別ファイルへ

「UTF-8 にすればいい」の一言で済まないのが Windows のつらいところだけど、化ける場所を分けて考えると、毎回同じ所で悩まなくて済む。PowerShell 7 に上げられるなら、それが結局いちばんの近道ではある。