バイトオーダーマーク(BOM)とは? 解説
バイトオーダーマーク(BOM)は、テキストファイルのまさに先頭にある特別なシーケンスで、そのエンコーディングとバイト順を示します。
バイトオーダーマークは、一部のエディタがテキストファイルの先頭に付ける、小さく不可視な接頭辞です。読み手がエンコーディングを検出するのを助けるためのものでしたが、実際には、はぐれたBOMは不可解なCIの失敗の頻繁な原因です - 「問題なさそう」なのに、まさに1行目でエラーになるスクリプトです。それが存在すると知っているだけで、何時間ものデバッグを節約できます。
BOMとは
バイトオーダーマークは、ファイルの先頭にある特定のバイト列で、そのUnicodeエンコーディングと、マルチバイトエンコーディングの場合はバイト順を示します。それは内容ではなくメタデータです - テキストが表示されるときは不可視ですが、バイトは実際にそこにあります。
UTF-16とUTF-8におけるBOM
UTF-16では、BOMは本当に有用です。バイトがビッグエンディアンかリトルエンディアンかを読み手に伝えます。UTF-8ではバイト順の曖昧さがないため、UTF-8のBOMは何の実際の目的も果たさず、省くのが最善です - それでも一部のWindowsエディタはとにかく付けてしまいます。
UTF-8のBOMがバグを引き起こす理由
多くのツールはBOMを予期しておらず、その先頭バイトを内容の一部として扱います。BOM付きのshellスクリプトは、shebangの前のゴミにインタープリタがつまずくため失敗します。JSONやYAMLのパーサーはファイルを拒否しうります。CSVのヘッダーは不可視な文字を帯びます。エラーメッセージがBOMに言及することはめったにありません。
CIにおけるBOM
ローカルでは動くのに、runner上で謎の「command not found」や1行目の構文エラーで失敗するスクリプトは、しばしばBOMの問題です。BOMを取り除くlintやpre-commitのルール、あるいはそれを拒否するCIのチェックが、ファイルがrunnerに到達するのを未然に防ぎます。
# Fail CI if any tracked file has a UTF-8 BOM
steps:
- run: |
if grep -rlP "^\xEF\xBB\xBF" --include="*.sh" .; then
echo "BOM found in shell script"; exit 1
fi実践的なアドバイス
特にshellスクリプト、JSON、YAML、CSVについては、ファイルをBOMなしのUTF-8で保存するようエディタを設定します。1行目のエラーが意味をなさないときは、隠れたBOMがないか確認することが、リストの上位近くにあるべきです。
重要なポイント
- BOMは、ファイルのエンコーディングと、UTF-16の場合はそのバイト順を示す、不可視な先頭バイトです。
- UTF-8のBOMは無意味であり、shellスクリプト、JSON、YAML、CSVのパースをしばしば壊します。
- CIでは、不可解な1行目のエラーは、しばしばはぐれたBOMです - ファイルをBOMなしのUTF-8で保存します。