できない.dev

PowerShell で出力したファイルが文字化けする(UTF-8 で保存できない)

Windows PowerShell 5.1 は cmdlet ごとに既定エンコーディングが違う。Out-File と > は UTF-16LE、Set-Content は ANSI になるため、-Encoding の明示か $PSDefaultParameterValues での既定変更が必要である。

公開: 更新:

実行例あり(2026-09-29 に実環境で検証)

要約

PowerShell で書き出した JSON や CSV を他のツールに渡すと文字化けする、あるいは先頭に見えない文字が混じる。
原因は Windows PowerShell 5.1 の既定エンコーディングが cmdlet ごとに違うことである。

"あいうえお" | Out-File data.txt
Get-Content data.txt -Encoding Byte -TotalCount 4
255
254
66
48

先頭の 255 254 は UTF-16LE の BOM である。
UTF-8 のつもりで書いたテキストが実際には UTF-16LE になっている。

公式ドキュメント(新しいタブで開く)が明記しているとおり、5.1 では Out-File と > >> が UTF-16LE、Set-Content と Add-Content が ANSI という別々の既定を持つ。
エンコーディングを常に明示するのが唯一確実な対処である。

実行例

PowerShell 7.4.2 のコンテナは既定がすでに BOM 無しの UTF-8 なので、5.1 の Out-File の既定に当たる UTF-16LE を -Encoding unicode で明示して書き出している。
BOM(377 376)と NUL が並ぶこのファイルは Get-Content では読めても UTF-8 としてデコードすると ConvertFrom-Json が失敗し、-Encoding utf8 で書き直すと読めるようになる一方、utf8BOM で書くと先頭に ef bb bf が残る。

$ $PSVersionTable.PSVersion.ToString()
7.4.2
$ @{ name = "あいうえお" } | ConvertTo-Json | Out-File data.json -Encoding unicode
$ od -c data.json | head -3
0000000 377 376   {  \0  \n  \0      \0      \0   "  \0   n  \0   a  \0
0000020   m  \0   e  \0   "  \0   :  \0      \0   "  \0   B   0   D   0
0000040   F   0   H   0   J   0   "  \0  \n  \0   }  \0  \n  \0
$ cat -v data.json
M-^?M-~{^@
^@ ^@ ^@"^@n^@a^@m^@e^@"^@:^@ ^@"^@B0D0F0H0J0"^@
^@}^@
^@
$ Get-Content data.json -Encoding utf8 | ConvertFrom-Json
name
----
あいうえお
$ $b = [IO.File]::ReadAllBytes("$PWD/data.json"); [Text.Encoding]::UTF8.GetString($b) | ConvertFrom-Json
ConvertFrom-Json: Conversion from JSON failed with error: Unexpected character encountered while parsing value: �. Path '', line 0, position 0.
$ @{ name = "あいうえお" } | ConvertTo-Json | Out-File data.json -Encoding utf8
$ (Get-Content data.json -Encoding utf8 | ConvertFrom-Json).name
あいうえお
$ od -c data.json | head -2
0000000   {  \n           "   n   a   m   e   "   :       " 343 201 202
0000020 343 201 204 343 201 206 343 201 210 343 201 212   "  \n   }  \n
$ $PSDefaultParameterValues["*:Encoding"] = "utf8"; "あいうえお" > redirect.txt; Get-Content redirect.txt -Encoding utf8
あいうえお
$ "あいうえお" | Out-File bom.txt -Encoding utf8BOM
$ od -An -tx1 -N 6 bom.txt
 ef bb bf e3 81 82
$ $utf8NoBom = [System.Text.UTF8Encoding]::new($false); [System.IO.File]::WriteAllText("$PWD/nobom.txt", "あいうえお", $utf8NoBom)
$ od -An -tx1 -N 6 nobom.txt
 e3 81 82 e3 81 84

— 2026-09-29 時点の出力

検証環境

検証日
実行環境
mcr.microsoft.com/powershellUbuntu 22.04.4 LTS

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. Out-File と > は UTF-16LE: 「> でリダイレクトしただけ」でも UTF-16LE になる。
    Git の diff がバイナリ扱いになる、grep が引っかからない、といった形で表面化する。
  2. Set-Content は ANSI: 対象ファイルが空か存在しないとき、システムのアクティブなコードページ(日本語環境なら CP932)で書かれる。
    同じスクリプト内で Out-File と Set-Content を混ぜると、ファイルごとに違うエンコーディングになる。
  3. 5.1 の UTF8 は BOM 付き: 5.1 では UTF7 を除くすべての Unicode エンコーディングが BOM を付ける。-Encoding UTF8 を指定しても BOM が入るため、BOM を解釈しない Unix 系ツールが先頭を不正な文字として読む。
  4. 読み込み側も既定が ANSI: Get-Content は BOM の無いファイルを ANSI として読む。
    UTF-8(BOM 無し)で保存されたファイルを読んだ時点で内部表現が壊れており、書き戻すと化けが確定する。
  5. 追記でエンコーディングが混ざる: Out-File -Append と >> は既存ファイルのエンコーディングに合わせず既定を使う。
    1 つのファイルの途中からエンコーディングが変わる、という壊れ方をする。

解決策

1. 常に -Encoding を明示する

"あいうえお" | Out-File data.txt -Encoding utf8
$obj | ConvertTo-Json | Set-Content data.json -Encoding utf8

読み込み側も同様に明示する。

Get-Content data.json -Encoding utf8 | ConvertFrom-Json

2. セッションの既定をまとめて変える

エンコーディング パラメーターを持つすべての cmdlet の既定を一括で変えられる。
5.1 以降は > と >> も内部で Out-File を呼ぶため、これでリダイレクトにも効く。

$PSDefaultParameterValues['*:Encoding'] = 'utf8'

スクリプトの先頭かプロファイルに置く。
ただしこれはセッション全体に効く設定なので、他人の環境や別バージョンでも同じ挙動にしたいなら、スクリプト側にも同じ行を入れておく。

3. 5.1 で BOM 無し UTF-8 を書く

5.1 の -Encoding UTF8 では BOM を外せない。
BOM 無しが必要なら .NET のメソッドを直接呼ぶ。

$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
[System.IO.File]::WriteAllText("$PWD\data.json", $json, $utf8NoBom)

WriteAllText は相対パスをカレントディレクトリではなくプロセスの作業ディレクトリで解決するため、$PWD を付けた絶対パスを渡す。

4. PowerShell 7 に移行する

7 系はすべての出力の既定が utf8NoBOM に統一されている。
cmdlet ごとの差もなくなるので、混在環境を抱えずに済むならこれが根本解決になる。
移行後もスクリプトを 5.1 と共用するなら、-Encoding の明示は残しておくとよい。

この記事は役立ちましたか?