1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PowerShellスクリプトの中身が、そのまま画面に出力された。原因はBOMでした

1
Posted at

個人開発をしています。コードはほとんど自分では書いていなくて、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 です。今回のスクリプトは、作っているゲームを配るための準備で書いたものでした。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?