出现症状的示例命令:sed 's/./@/' <<<$'\xfc'
失败,因为byte 0xfc
不是有效的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 ...
将正常也工作,但如果LC_ALL
恰好是集(比其他东西C
),它将覆盖单个LC_*
-category变量,如LC_CTYPE
。因此,最可靠的方法是set LC_ALL
。
然而,(有效)的设置LC_CTYPE
,以C
对待字符串,就好像每个字节是它自己的特点(没有进行基于编码规则的解释),有没有考虑对-多字节点播- UTF-8编码的是OS X采用默认,其中外来字符具有多字节编码。
简而言之:设置LC_CTYPE
为C
会使外壳程序和实用程序仅将基本的英语字母识别为字母(7位ASCII范围内的字母),以便使用外字符。不会被视为 letter,例如,导致大写/小写转换失败。
同样,如果您不需要匹配多字节编码的字符(例如)é
,而只是想将这些字符传递通过,则可能会很好。
如果这还不够,并且/或者您想了解原始错误的原因(包括确定导致问题的输入字节)并按需执行编码转换,请阅读以下内容。
问题在于输入文件的编码与外壳程序的编码不匹配。
更具体地说,输入文件包含的字符编码方式在UTF-8中无效(如@KlasLindbäck在评论中所述),这就是sed
错误消息试图通过的方式invalid byte sequence
。
您的输入文件最有可能使用单字节8位编码(例如)ISO-8859-1
,经常用于编码“西欧”语言。
例:
重音字母à
具有Unicode代码点0xE0
(224)-与中的相同ISO-8859-1
。然而,由于本质UTF-8编码,该单个码点被表示为2个字节- 0xC3 0xA0
,而试图通过该单字节 0xE0
是无效下UTF-8。
这是使用编码为的字符串并用一个字节表示的问题的演示(通过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
列出所有受支持的。
例子:
在上述示例的ISO-8859-1
基础上LC_CTYPE
,将FROM转换为外壳中有效的编码(基于,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