如何检测一个文件是否在Bash中有一个UTF-8 BOM?

我正在尝试编写一个能够自动从文件中删除UTF-8 BOM的脚本。 我无法检测文件是否有一个在第一个地方。 这是我的代码:

function has-bom { # Test if the file starts with 0xEF, 0xBB, and 0xBF head -c 3 "$1" | grep -P '\xef\xbb\xbf' return $? } 

出于某种原因, head似乎忽略了文件前面的BOM。 举个例子,运行这个

 printf '\xef\xbb\xbf' > file head -c 3 file 

将不会打印任何东西。

我试图寻找一个选项head --help ,让我解决这个问题,但没有运气。 有什么我可以做的这个工作?

首先,让我们来证明这个head实际上正在工作:

 $ printf '\xef\xbb\xbf' >file $ head -c 3 file $ head -c 3 file | hexdump -C 00000000 ef bb bf |...| 00000003 

现在,我们来创建一个工作函数has_bom 。 如果你的grep支持-P ,那么一个选项是:

 $ has_bom() { head -c3 "$1" | LC_ALL=C grep -qP '\xef\xbb\xbf'; } $ has_bom file && echo yes yes 

目前,只有GNU grep支持-P

另一种选择是使用bash的$'...'

 $ has_bom() { head -c3 "$1" | grep -q $'\xef\xbb\xbf'; } $ has_bom file && echo yes yes 

kshzsh也支持$'...'但是这个构造不是POSIX,而且dash不支持它。

笔记:

  1. 使用显式return $? 是可选的。 该函数默认情况下会返回上一个命令运行的退出代码。

  2. 我用POSIX表格来定义函数。 这相当于bash窗体,但是如果你需要在另外一个shell下运行这个函数,那么这个窗口就不会有问题了。

  3. bash确实接受使用这个字符-在一个函数名中,但是这是一个有争议的特性。 我用更广泛接受的_取代了它。 (有关此问题的更多信息,请参阅此答案 。)

  4. grep-q选项很安静,这意味着它仍然设置了正确的退出代码,但是它不会向stdout发送任何字符。