我正在尝试替换Mac OS X上的Makefile中的字符串,以便交叉编译到iOS。该字符串嵌入了双引号。命令是:
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
我有一点时间调试问题。有谁知道如何让sed
打印非法字节序列的位置?或者有谁知道非法字节序列是什么?
答案 0 :(得分:260)
显示症状的示例命令:sed 's/./@/' <<<$'\xfc'
失败,因为字节0xfc
不是有效的UTF-8字符。
请注意,相比之下, GNU sed
(Linux,但也可以在macOS上安装)只是传递无效字节,而不报告错误。
如果你不介意失去对真正语言环境的支持,那么使用 formerly accepted answer是一个选项(如果你在美国系统上而且你永远不需要处理外国字符,这可能没问题。)
但是,只有单一命令的 ad-hoc 相同的效果 :
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
会将字符串视为每个字节都是其自己的字符(没有基于编码的解释执行规则),不考虑用于 - 默认情况下OS X使用的 - 多字节按需 - UTF-8编码,其中外来字符具有多字节编码。
简而言之:将LC_CTYPE
设置为C
会导致shell和实用程序仅将基本英文字母识别为字母(7位ASCII范围内的字母) ,以便外国人。不会被视为字母,导致例如大写/小写转换失败。
同样,如果您不需要匹配多字节编码的字符(例如é
),并且只是希望通过传递此类字符,这可能会很好。
如果这还不够和/或您希望了解原始错误的原因(包括确定导致问题的输入字节)和按需执行编码转换 ,在下面阅读。
问题是输入文件的编码与shell的编码不匹配
更具体地说,输入文件包含以UTF-8无效的方式编码的字符(正如@KlasLindbäck在评论中所述) - 这就是sed
错误消息正在尝试的内容由invalid byte sequence
说。
最有可能的是,您的输入文件使用单字节8位编码,例如ISO-8859-1
,经常用于编码“西欧”语言。
示例:
重音字母à
具有Unicode代码点0xE0
(224) - 与ISO-8859-1
中的相同。但是,由于 UTF-8 编码的性质,此单个代码点表示为 2 字节 - 0xC3 0xA0
,而尝试传递单个UTF-8下的字节 0xE0
无效。
以下是使用编码为voilà
的字符串ISO-8859-1
的演示问题,其中à
表示为一个字节(通过使用$'...'
创建字节的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
列出了所有受支持的内容。
<强>示例:强>
基于上面的示例,将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
答案 1 :(得分:124)
将以下行添加到~/.bash_profile
或~/.zshrc
文件中。
export LC_CTYPE=C
export LANG=C
答案 2 :(得分:3)
mklement0's answer很棒,但我有一些小调整。
使用bash
时明确指定iconv
的编码似乎是个好主意。此外,我们应该在even though the unicode standard doesn't recommend it之前添加一个字节顺序标记(there can be legitimate confusions between UTF-8 and ASCII without a byte-order mark)。不幸的是,iconv
在您明确指定字节顺序(UTF-16BE
或UTF-16LE
)时不会添加字节顺序标记,因此我们需要使用UTF-16
,它使用平台特定的字节顺序,然后使用file --mime-encoding
来发现使用的真实字节序iconv
。
(我的所有编码都是大写的,因为当你用iconv
列出所有iconv -l
支持的编码时,它们都是大写的。)
# 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
答案 3 :(得分:2)
我的解决方法一直使用Perl:
find . -type f -print0 | xargs -0 perl -pi -e 's/was/now/g'
答案 4 :(得分:1)
您只需要在 sed 命令之前通过管道传递 iconv 命令。 例如,使用file.txt输入:
iconv -f ISO-8859-1 -t UTF8-MAC file.txt | sed's / something /àéèêçùû/ g'| .....
-f 选项是'from'代码集,-t选项是'to'代码集转换。
注意大小写,网页通常显示小写字母,如
UTF8-MAC 是用于转换的现代OS Mac代码集。
答案 5 :(得分:1)
有人知道如何打印非法字节序列的位置吗?还是有人知道非法字节序列是什么?
$ uname -a
Darwin Adams-iMac 18.7.0 Darwin Kernel Version 18.7.0: Tue Aug 20 16:57:14 PDT 2019; root:xnu-4903.271.2~2/RELEASE_X86_64 x86_64
我只是使用 tr 来回答上述问题。
我有一个.csv文件,它是一张信用卡对帐单,我正在尝试将其导入Gnucash。我住在瑞士,所以我必须处理苏黎世之类的词。怀疑Gnucash在数字字段中不喜欢“”,我决定只替换所有
; ;
使用
;;
在这里:
$ head -3 Auswertungen.csv | tail -1 | sed -e 's/; ;/;;/g'
sed: RE error: illegal byte sequence
我使用 od 进行了一些说明:请注意此 od -c 输出中的374
$ head -3 Auswertungen.csv | tail -1 | od -c
0000000 1 6 8 7 9 6 1 9 7 1 2 2 ; 5
0000020 4 6 8 8 7 X X X X X X 2 6
0000040 6 0 ; M Y N A M E I S X ; 1
0000060 4 . 0 2 . 2 0 1 9 ; 9 5 5 2 -
0000100 M i t a r b e i t e r r e s t
0000120 Z 374 r i c h
0000140 C H E ; R e s t a u r a n t s ,
0000160 B a r s ; 6 . 2 0 ; C H F ;
0000200 ; C H F ; 6 . 2 0 ; ; 1 5 . 0
0000220 2 . 2 0 1 9 \n
0000227
然后我想我可以说服 tr 代替374来代替正确的字节码。所以首先我尝试了一些简单的方法,虽然没有用,但是却产生了副作用,告诉我麻烦的字节在哪里:
$ head -3 Auswertungen.csv | tail -1 | tr . . ; echo
tr: Illegal byte sequence
1687 9619 7122;5468 87XX XXXX 2660;MY NAME ISX;14.02.2019;9552 - Mitarbeiterrest Z
您可以看到374个字符的 tr 保释金。
使用perl似乎可以避免此问题
$ head -3 Auswertungen.csv | tail -1 | perl -pne 's/; ;/;;/g'
1687 9619 7122;5468 87XX XXXX 2660;ADAM NEALIS;14.02.2019;9552 - Mitarbeiterrest Z?rich CHE;Restaurants, Bars;6.20;CHF;;CHF;6.20;;15.02.2019
答案 6 :(得分:0)
我的解决方法一直在使用gnu sed
。为我的目的工作得很好。