個人開発をしています。コードはほとんど自分では書いていなくて、AIと一緒に作っています。
今日、日本語のコメントをたくさん書いた PowerShell スクリプトを実行したら、こうなりました。
荳翫£繧倶クュ霄ォ: 211.7 MB
GeckoChase.exe
--- 荳玖ヲ九Δ繝シ繝会シ医い繝・・繝ュ繝シ繝峨@縺ェ縺・シ・--
}
if () {
Write-Host
-StageOnly なので steamcmd は呼ばない。中身と設定ファイルの確認まで。
文字化けはまだ分かります。でも下の方、if () { とか Write-Host とか、スクリプトのソースコードがそのまま出力されています。
エラーも出ていません。原因は文字コードでした。
先に結論
Windows PowerShell 5.1 は、BOMが無いUTF-8の .ps1 を「ANSI(日本語環境ではShift_JIS系)」として読みます。
日本語のコメントや文字列が壊れて、壊れたバイト列がクォートやカッコとして解釈され、構文まで崩れる。その結果、スクリプトの一部が「ただの文字列」として扱われて出力に流れた、というわけです。
直し方はファイルをBOM付きUTF-8で保存するだけです。
BOMとは(念のため)
**BOM(Byte Order Mark)**は、ファイルの先頭に付ける3バイトの目印です。
EF BB BF ← これがあると「このファイルはUTF-8ですよ」の合図になる
中身としては見えません。テキストエディタでは何も表示されない。でもプログラムがファイルを開くときに「どの文字コードで読むか」を決める手がかりになります。
Windows PowerShell 5.1 の場合:
| ファイルの先頭 | 5.1 の解釈 |
|---|---|
| BOM あり | ⭕ UTF-8 として読む |
| BOM なし | ❌ ANSI として読む(日本語環境なら Shift_JIS 系) |
英語のコメントしか書いていなければ、ASCII の範囲なのでどちらで読んでも同じ結果になります。日本語を書いた瞬間に壊れます。
★ちなみに PowerShell 7 以降は BOM なしを UTF-8 として読みます。 なので「7では動くのに5.1で壊れる」という形で出会うことが多いです。Windows 10/11 に最初から入っているのは 5.1 のほうなので、配る前提のスクリプトほど踏みやすい。
確認のしかた
先頭3バイトを見るだけです。
[System.IO.File]::ReadAllBytes("script.ps1")[0..2] -join ','
# 239,187,191 と出れば BOM 付き(EF BB BF の10進表記)
直しかた
$p = "script.ps1"
$t = [System.IO.File]::ReadAllText($p, [System.Text.Encoding]::UTF8)
[System.IO.File]::WriteAllText($p, $t, (New-Object System.Text.UTF8Encoding($true)))
# ↑ true で BOM を付ける
VS Code なら、右下のエンコーディング表示をクリックして
「エンコード付きで保存」→「UTF-8 with BOM」 でも同じことができます。
⚠️ 逆に「BOMを付けてはいけない」ファイルもある
ここが厄介なところで、同じフォルダの中でも、付けるファイルと付けないファイルが混ざります。
私の場合、.ps1 は BOM 付きにしたいのですが、そのスクリプトが書き出す設定ファイル(Steam に渡す .vdf)は BOM なしでないといけません。BOM の3バイトを設定ファイルの先頭に置くと、読む側のパーサーが1文字目でつまずくことがあるからです。
なので、書き出すときは明示的に BOM なしを指定しました。
# ⚠️ Set-Content -Encoding UTF8 は PowerShell 5.1 だと BOM を付けてしまう
[System.IO.File]::WriteAllText($path, $text, (New-Object System.Text.UTF8Encoding($false)))
# ↑ false で BOM なし
5.1 の -Encoding UTF8 は BOM 付きです。ここも 7 とは挙動が違います(7 は utf8NoBOM が既定)。「UTF8って書いたのに」と思ったら、まずここを疑ってください。
まとめ
- Windows PowerShell 5.1 は BOM が無い
.ps1を ANSI として読む - 日本語を書いていると文字化けだけでなく構文まで壊れる。エラーが出ないので原因に見えづらい
.ps1は BOM 付き UTF-8 で保存する- ⚠️ 他のプログラムに読ませるファイルは、逆に BOM を付けない。
Set-Content -Encoding UTF8は 5.1 だと BOM 付きになるので、WriteAllTextで明示する - 確認は先頭3バイトが
239,187,191かどうか
「エラーが出ないのに挙動がおかしい」ときは、文字コードを疑ってみてください。私は今回、しばらくスクリプトのロジックを疑っていて遠回りしました。
普段は ShibaHub という無料の計算ツールを集めたサイトや、補助金ナビ を作っています。屋号は sesebox です。今回のスクリプトは、作っているゲームを配るための準備で書いたものでした。