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

Linux 如何在Bash中检测文件是否具有UTF-8 BOM?,linux,bash,unix,encoding,utf-8,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' &

我正在尝试编写一个脚本,该脚本将自动从文件中删除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\u 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
ksh
zsh
也支持
$“…”
但此构造不是POSIX,并且
dash
不支持它

注:

  • 使用显式
    返回$?
    是可选的。默认情况下,该函数将返回上次命令运行的退出代码

  • 我使用POSIX表单定义函数。这相当于bash表单,但是如果您必须在另一个shell下运行函数,那么您可以少处理一个问题

  • bash确实接受在函数名中使用字符
    -
    ,但这是一个有争议的特性。我将其替换为更为广泛接受的
    。(有关此问题的更多信息,请参阅。)

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


  • 我在第一行中应用了以下内容:

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

    这只是从变量中删除BOM。

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

    function has_bom() {
        local bom
        LANG=C read -r -N 3 bom < "$1"
        [[ "$bom" == $'\xef\xbb\xbf' ]]
    }
    
    无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
    

    哈,从来都不知道Bash支持十六进制字符串文字。无论如何,谢谢你的回答!您好,
    head-c3文件| hextump-c
    行中的
    -c
    是做什么的?前一个似乎是1)限制输出的字符数2)限制行号(可能)为0000000和0000003;但后者将输出(应该是“be bf”等)变成替换标记。我正在使用bash并在Windows下生成的文本文件上进行测试,原始编码=GB18030。谢谢。@CrazyFrog
    head-c3文件
    文件
    的前三个字符写入标准输出
    hextump-C
    以人性化的方式将这些字符格式化为十六进制。@John1024谢谢我找到了手册!虽然我在文本文件的开头用代码生成BOM表,但这很奇怪,但是这个命令没有看到它。@John1024这可能是我应该做的。我将自己再次检查代码。我一定是弄错了。谢谢你的帮助!
    $ 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