🐔

BOM 付き UTF-8 のファイルを探す

2023/08/18に公開

ついカッとなって書いた。
後悔はしていないが公開はする(爆笑

BOM って何だっけ?

最近流行りの SBOM ではない。
UTF-8 の BOM である。
お前 BOM って何の略だか分かってんのか?と小一時間問い詰めたくなる UTF-8 の BOM である。

しかし Windows なんかは BOM が無いと UTF-8 と判断してくれないアプリがあったり、何なら BOM の無いファイルでも編集するとご丁寧に BOM を付けてくれやがるアプリがあったりするので、知らないうちに紛れ込んだりする。
あるいは、もしかしたらあなたの親切な同僚が GitHub に上がっているソースにいつの間にか BOM を付けてコミットしてくれているかもしれない(フィクションです)。

と言う訳で BOM 付き UTF-8 のファイルが紛れ込んでないか確認したくなることが稀によくある。

file コマンドさんはすごいぞ

で、UTF-8 の BOM があるファイルを探し出すコマンドをググって見ると file コマンドで見る方法が多くヒットする。
例えば C# のソース(VS とかで編集されるので BOM 率が高いがち)から探す場合はこんな感じ。

find . -name '*.cs' -exec file -e encoding {} + | grep "(with BOM)"

いや全く以て正しい。
正しいんだが file コマンドはちょっとオーバーキルじゃないだろうか?

ヤツは指定したファイルが何のファイルなのか(C 言語のソースなのか、とか)を判断しようとするので、ファイルの中身を一生懸命読んでうんうん考える(そんなに考えてないか?)。
つまり遅い。
上記では -e encoding オプションを指定してエンコーディングだけを判断するようにしているので多少は速くなってるんじゃないかとは思うが、それでもやっぱり遅い。

BOM なんてファイルの先頭に付いてるんだからそんなに一生懸命読まなくても良くない?

助けてドラえもん

と言う訳でドラえもんワンライナーである。
最初何で書くか 3 秒ぐらい悩んだが、ジジイなのでつい長らく慣れ親しんだ Perl にしてしまった。

find . -name '*.cs' -exec perl -nE 'say$ARGV if/^\xef\xbb\xbf/;close ARGV' {} +

file コマンドよりちょっと長いが、やっぱりこっちの方が断然速いしまぁ許容範囲じゃなかろうか。

見たまんまなので解説は要らないとは思うが、縁起ものなので軽く。

  • say$ARGV はファイル名を表示。文修飾子 if/^\xef\xbb\xbf/ が付いてるので行の先頭が BOM だったら表示される。
  • close ARGV で次のファイルに進む。

極めてシンプルでしょ?

perl コマンドって何ですか?

イマドキ Perl とか使ってんじゃねぇよジジイ!と言う声が聞こえた気がしたので(幻聴)、AWK でも同じのを書いてみた。

find . -name '*.cs' -exec awk '/^\357\273\277/{print FILENAME}{nextfile}' {} +

何と AWK の方が短くなった。マジか。

こちらは Perl 版以上に見たまんまだが、一応。

  • {print FILENAME} はファイル名を表示。パターンとして /^\357\273\277/ が付いてるので行の先頭が BOM だったら表示される。8 進表記なのは POSIX では \xHH が使えないから。
  • {nextfile} で次のファイルに進む。

こちらも極めてシンプル。

まぁ速いんだろうけど…

Perl にしろ AWK にしろそんなん覚えてられねぇよ、と言われそう…(特に BOM のコード部分

まぁよく使うのであれば Perl とか AWK の部分はスクリプトファイルにでもしといて下さい。

find-bom.pl
#!/usr/bin/perl -n
print "$ARGV\n" if /^\xef\xbb\xbf/;
close ARGV;
find-bom.awk
#!/usr/bin/awk -f
/^\357\273\277/ { print FILENAME }
{ nextfile }

おまけ

BOM の削除はこんな感じで。

find . -name '*.cs' -exec sed -i '1s/^\xEF\xBB\xBF//' {} +

もし sed-i オプション使えなかったらゴメン、ちょっと長いけど Perl でも使って…

find . -name '*.cs' -exec perl -i -pe 's/^\xEF\xBB\xBF// if$.eq 1;close ARGV if eof' {} +

でもこれだと全ファイル編集しちゃって遅いかもなので、もし BOM 付いてるのだけを対象にして速くしたいのであれば find の替わりに xargs で前述の BOM 付きファイル発見コマンドの出力と組み合わせて使ってクレメンス。(でもほとんどのファイルが BOM 削除対象だったらむしろ遅くなるかもしれん…)

ところで BOM 付けたいなんて人はいないよね?(威圧

Discussion