【问题标题】:How to detect if a file has a UTF-8 BOM in Bash?如何在 Bash 中检测文件是否具有 UTF-8 BOM?
【发布时间】:2015-11-28 23:57:17
【问题描述】:

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

【问题讨论】:

    标签: linux bash unix encoding utf-8


    【解决方案1】:

    首先,让我们证明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. grep-q 选项使其安静,这意味着它仍然设置正确的退出代码,但不会将任何字符发送到标准输出。

    【讨论】:

    • 呵呵,从来不知道 Bash 支持十六进制字符串文字。无论如何,感谢您的出色回答!
    • 嗨,请问head -c 3 file | hexdump -c -c 是做什么的?前一个似乎是1)限制输出的字符数2)将行号(可能)限制为0000000和0000003;但后者使输出(应该是“be bf”等)成为替换标记。我正在使用 bash 并测试在 Windows 下生成的文本文件,原始编码 = GB18030。谢谢。
    • @CrazyFrog head -c 3 filefile 的前三个字符写入标准输出。 hexdump -C 以对人类友好的方式将这些字符格式化为十六进制。
    • @John1024 谢谢我找到了手册!奇怪的是,虽然我在文本文件的开头使用代码生成特定的 BOM,但这个命令看不到它。
    • @John1024 这可能是我应该做的。我将再次自己查看代码。我一定有一个错误。感谢您的帮助!
    【解决方案2】:

    我对第一行应用了以下内容:

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

    这只是从变量中删除 BOM。

    【讨论】:

      【解决方案3】:

      在纯 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
      

      【讨论】:

        猜你喜欢
        • 2013-12-04
        • 2020-12-09
        • 2012-04-26
        • 1970-01-01
        • 1970-01-01
        • 2016-05-04
        • 2010-09-12
        • 2016-05-03
        • 2017-05-08
        相关资源
        最近更新 更多