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-16BE
或UTF-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
。 为我的目的工作得很好。