我正在尝试编写一个脚本,该脚本会自动从文件中删除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
寻找一个让我解决这个问题的选项,但没有运气。我有什么办法可以做到这一点吗?
答案 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
ksh
和zsh
也支持$'...'
,但此构造不是POSIX,dash
不支持它。
注意:
使用显式return $?
是可选的。默认情况下,该函数将返回上一个命令运行的退出代码。
我使用POSIX表单来定义函数。这相当于bash表单,但如果你必须在另一个shell下运行该函数,则可以减少一个问题。
bash确实接受在函数名中使用字符-
,但这是一个有争议的功能。我将其替换为_
,这被广泛接受。 (有关此问题的更多信息,请参阅this answer。)
-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