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