显示以下症状的示例命令:sed 's/./@/' <<<$'\xfc'
失败,因为 byte0xfc
不是有效的 UTF-8 字符。
请注意,相比之下,GNU sed
(Linux,但也可安装在 macOS 上)只是简单地传递无效字节,而不会报告错误。
如果您不介意失去对真实语言环境的支持,则可以选择使用以前接受的答案(如果您在美国系统上并且您永远不需要处理外来字符,那可能没问题。)
但是,仅对单个命令可以产生相同的效果:
LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
注意:重要的是 的有效 LC_CTYPE
设置,C
因此LC_CTYPE=C sed ...
通常也可以工作,但如果碰巧设置(设置为 以外的其他值),它将覆盖单个-category 变量,例如. 因此,最稳健的方法是设置.LC_ALL
C
LC_*
LC_CTYPE
LC_ALL
但是,(有效地)设置LC_CTYPE
将C
字符串视为每个字节都是其自己的字符(不执行基于编码规则的解释),而不考虑OS X 默认采用的 - multibyte-on-demand - UTF-8 编码,其中外来字符具有多字节编码。
简而言之:设置LC_CTYPE
为C
使 shell 和实用程序仅将基本英文字母识别为字母(7 位 ASCII 范围内的字母),因此外来字符。不会被视为字母,例如导致大写/小写转换失败。
同样,如果您不需要匹配多字节编码的字符(例如é
,并且只是想通过.
如果这还不够和/或您想了解原始错误的原因(包括确定导致问题的输入字节)并按需执行编码转换,请继续阅读下文。
问题是输入文件的编码与 shell 的不匹配。
更具体地说,输入文件包含以在 UTF-8 中无效的方式编码的字符(正如@Klas Lindbäck 在评论中所述) - 这就是sed
错误消息试图通过invalid byte sequence
.
最有可能的是,您的输入文件使用单字节 8 位编码,例如ISO-8859-1
,经常用于编码“西欧”语言。
例子:
重音字母à
具有 Unicode 代码点0xE0
(224) - 与ISO-8859-1
. 但是,由于UTF-8编码的性质,这个单个代码点表示为2个字节 - ,而在 UTF-8 下0xC3 0xA0
尝试传递单个字节 0xE0
是无效的。
下面是使用编码为的字符串的问题演示,表示为一个字节(通过用于创建字节的 ANSI-C 引用的 bash 字符串 ( ) ):voilà
ISO-8859-1
à
$'...'
\x{e0}
请注意,该sed
命令实际上是一个简单地传递输入的无操作,但我们需要它来引发错误:
# -> '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
列出所有支持的。
例子:
在上面的示例的基础上,将 FROM 转换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}')"
要将输入 BACK 转换为ISO-8859-1
后处理,只需将结果通过管道传递给另一个iconv
命令:
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1