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

时间:2015-11-28 23:57:17

标签: linux bash unix encoding utf-8

我正在尝试编写一个脚本,该脚本会自动从文件中删除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寻找一个让我解决这个问题的选项,但没有运气。我有什么办法可以做到这一点吗?

3 个答案:

答案 0 :(得分:17)

首先,让我们演示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确实接受在函数名中使用字符-,但这是一个有争议的功能。我将其替换为_,这被广泛接受。 (有关此问题的更多信息,请参阅this answer。)

  4. -q的{​​{1}}选项使其保持安静,这意味着它仍会设置正确的退出代码,但不会向stdout发送任何字符。

答案 1 :(得分:0)

我为第一条读取线应用了以下内容:

read c
if (( "$(printf "%d" "'${c:0:1}")" == 65279 ))  ; then c="${c:1}" ; fi

这只是从变量中删除BOM。

答案 2 :(得分:0)

在纯 bash 中,解决方案可能是:

function has_bom() {
    local bom
    LANG=C read -r -N 3 bom < "$1"
    [[ "$bom" == $'\xef\xbb\xbf' ]]
}

使用带有 BOM 的文件进行测试:

$ F=test.with-bom
$ head -c 5 $F | hd
00000000  ef bb bf c3 a9                                    |.....|
$ has_bom "$F" && echo "$F has a BOM" || echo "$F has no BOM"
test.with-bom has a BOM

在没有 BOM 时进行测试:

$ F=test.utf8
$ head -c 5 "$F" | hd
00000000  c3 a9 6c c3 a9                                    |..l..|
$ has_bom "$F" && echo "$F has a BOM" || echo "$F has no BOM"
test.utf8 has no BOM