显示该症状的示例命令:sed 's/。/@/' <<<$'\xfc'失败,因为字节0xfc不是有效的UTF-8字符。
注意,相比之下,GNU sed (Linux,但也可安装在macOS上)只是简单地传递无效字节,而不报告错误。
如果你不介意失去对真实语言环境的支持,使用以前接受的答案是一个选择(如果你在美国系统上,你永远不需要处理外国字符,这可能是好的)。
然而,对于单个命令,ad-hoc也可以产生相同的效果:
LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
注意:重要的是C的有效LC_CTYPE设置,因此LC_CTYPE=C sed…通常也可以工作,但如果LC_ALL恰好被设置(为C以外的东西),它将覆盖个别LC_*-类别变量,如LC_CTYPE。因此,最健壮的方法是设置LC_ALL。
然而,(有效地)将LC_CTYPE设置为C将字符串视为每个字节都是它自己的字符(不执行基于编码规则的解释),而不考虑OS X默认采用的-多字节按需- UTF-8编码,其中外部字符具有多字节编码。
简而言之:将LC_CTYPE设置为C会导致shell和实用程序仅将基本的英语字母识别为字母(7位ASCII范围内的字母),因此外部字符。不会被视为字母,例如,会导致大写/小写转换失败。
同样,如果您不需要匹配多字节编码的字符(如é),而只是想传递这些字符,那么这样做也可以。
如果这还不够,并且/或者您想了解原始错误的原因(包括确定导致问题的输入字节)并根据需要执行编码转换,请阅读下面的内容。
问题是输入文件的编码与shell的编码不匹配。
更具体地说,输入文件包含以UTF-8无效的方式编码的字符(正如@Klas Lindbäck在注释中所述)——这就是sed错误消息试图通过无效的字节序列表示的内容。
很可能,您的输入文件使用单字节8位编码,如ISO-8859-1,经常用于编码“西欧”语言。
例子:
重音字母à具有Unicode代码点0xE0(224) -与ISO-8859-1相同。然而,由于UTF-8编码的性质,这个单个代码点表示为2个字节- 0xC3 0xA0,而试图传递单个字节0xE0在UTF-8下是无效的。
下面是使用ISO-8859-1编码的字符串voilà的问题演示,其中à表示为一个字节(通过ansi - c引用的bash字符串($'…'),使用\x{e0}创建字节):
注意,sed命令实际上是一个no-op,只是简单地传递输入,但我们需要它来引发错误:
# -> 'illegal byte sequence': byte 0xE0 is not a valid char.
sed 's/.*/&/' <<<$'voil\x{e0}'
为了简单地忽略这个问题,可以使用上面的LCTYPE=C方法:
# No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
如果你想确定输入的哪一部分导致了问题,试试下面的方法:
# Convert bytes in the 8-bit range (high bit set) to hex. representation.
# -> 'voil\x{e0}'
iconv -f ASCII --byte-subst='\x{%02x}' <<<$'voil\x{e0}'
输出将以十六进制形式显示具有高位集的所有字节(超过7位ASCII范围的字节)。(但请注意,这也包括正确编码的UTF-8多字节序列——需要更复杂的方法来明确识别UTF-8字节中的无效字节。)
按需执行编码转换:
标准实用程序iconv可用于转换(-t)和/或从(-f)编码;Iconv -l列出所有支持的。
例子:
从ISO-8859-1转换为shell中有效的编码(基于LC_CTYPE,默认是基于utf -8的),构建在上面的示例上:
# Converts to UTF-8; output renders correctly as 'voilà'
sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
注意,这种转换允许你正确匹配外部字符:
# Correctly matches 'à' and replaces it with 'ü': -> 'voilü'
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"
要在处理后将输入转换回ISO-8859-1,只需将结果管道到另一个iconv命令:
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1