RE错误:Mac OS X上的非法字节序列

我试图在Mac OS X上replaceMakefile中的string来交叉编译到iOS。 该stringembedded了双引号。 该命令是:

sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure 

而错误是:

 sed: RE error: illegal byte sequence 

我已经尝试了逃避双引号,逗号,破折号和冒号,没有喜悦。 例如:

 sed -i "" 's|\"iphoneos-cross\"\,\"llvm-gcc\:\-O3|\"iphoneos-cross\"\,\"clang\:\-Os|g' Configure 

我有一个时间debugging这个问题。 有谁知道如何让sed打印非法字节序列的位置? 还是有人知道什么是非法的字节序列?

显示症状的示例命令: sed 's/./@/' <<<$'\xfc' :失败,因为字节0xfc不是有效的UTF-8字符。
请注意,相比之下, GNU sed (Linux,也可以在macOS上安装)仅仅传递无效字节,而不报告错误。

如果您不介意丢失对真实语言环境的支持 (如果您使用的是美国系统,而且您永远不需要处理外来字符,则可以使用)。

但是, 同样的效果可以只针对一个命令

 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_* -categoryvariables作为LC_CTYPE 。 因此,最可靠的方法是设置LC_ALL

但是,(有效地)将LC_CTYPE设置为C将string视为每个字节都是自己的字符执行基于编码规则的解释), 而不考虑 OS X应用的UTF-8编码默认情况下, 外部字符具有多字节编码

简而言之: LC_CTYPE设置为C导致shell和实用程序仅将基本英文字母识别为字母(7位ASCII范围中的字母),以便使用外来字符。 将不会被视为字母 ,例如导致大写/小写转换失败。

同样,如果你不需要匹配多字节编码的字符(如é ,并且只是想传递这样的字符 ,那么这可能是好的。

如果这是不够的和/或你想了解原始错误的原因 (包括确定什么input字节导致的问题),并按需要执行编码转换 ,请阅读下面。


问题是input文件的编码与shell的不匹配。
更具体地说, input文件包含的字符编码方式在UTF-8中无效 (如@KlasLindbäck在注释中所述) – 这是sed错误消息试图通过invalid byte sequence

最有可能的是,您的input文件使用一个单字节的8位编码 ,如ISO-8859-1 ,经常用于编码“西欧”语言。

例:

重音字母à具有Unicode代码点0xE0 (224) – 与ISO-8859-1相同。 但是,由于UTF-8编码的性质,这个单一的代码点被表示为2个字节 – 0xC3 0xA0 ,而试图传递单个字节 0xE0在UTF-8下是无效的

下面是使用以ISO-8859-1编码的stringvoilà示例 ,其中à表示为一个字节(通过ANSI-C引用的bashstring( $'...' ),该string使用\x{e0}创build字节):

请注意, sed命令实际上是一个简单地传递input的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}' 

如果您想确定input的哪些部分导致问题 ,请尝试以下操作:

  # 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}' 

输出将以hexforms显示所有具有高位设置的字节(超过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}')" 

要处理后将inputBACK转换为ISO-8859-1 ,只需将结果传送给另一个iconv命令:

 sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1 

~/.bash_profile添加到~/.bash_profile~/.zshrc文件中。

 export LC_CTYPE=C export LANG=C 

mklement0的答案很好,但是我有一些小的调整。

在使用iconv时,明确指定bash的编码似乎是个好主意。 另外,我们应该在前面加一个字节顺序标记( 即使unicode标准不推荐它 ),因为在没有字节顺序标记的情况下UTF-8和ASCII之间可能存在合法的混淆 。 不幸的是,当你显式指定一个字节顺序( UTF-16BEUTF-16LE )时, iconv并不会预先设置一个字节顺序标记,所以我们需要使用UTF-16 ,它使用平台特定的字节顺序,然后使用file- file --mime-encoding发现真正的endianness iconv使用。

(我把所有的编码大写,因为当你用iconv -l列出所有iconv支持的编码时,它们都是大写的。)

 # Find out MY_FILE's encoding # We'll convert back to this at the end FILE_ENCODING="$( file --brief --mime-encoding MY_FILE )" # Find out bash's encoding, with which we should encode # MY_FILE so sed doesn't fail with # sed: RE error: illegal byte sequence BASH_ENCODING="$( locale charmap | tr [:lower:] [:upper:] )" # Convert to UTF-16 (unknown endianness) so iconv ensures # we have a byte-order mark iconv -f "$FILE_ENCODING" -t UTF-16 MY_FILE > MY_FILE.utf16_encoding # Whether we're using UTF-16BE or UTF-16LE UTF16_ENCODING="$( file --brief --mime-encoding MY_FILE.utf16_encoding )" # Now we can use MY_FILE.bash_encoding with sed iconv -f "$UTF16_ENCODING" -t "$BASH_ENCODING" MY_FILE.utf16_encoding > MY_FILE.bash_encoding # sed! sed 's/.*/&/' MY_FILE.bash_encoding > MY_FILE_SEDDED.bash_encoding # now convert MY_FILE_SEDDED.bash_encoding back to its original encoding iconv -f "$BASH_ENCODING" -t "$FILE_ENCODING" MY_FILE_SEDDED.bash_encoding > MY_FILE_SEDDED # Now MY_FILE_SEDDED has been processed by sed, and is in the same encoding as MY_FILE 

我的解决方法是使用gnu sed 。 为我的目的工作得很好。