REエラー:Mac OS Xでの不正なバイトシーケンス


184

iOSへのクロスコンパイルのためにMac OS X上のMakefile内の文字列を置き換えようとしています。文字列には二重引用符が埋め込まれています。コマンドは次のとおりです。

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不正なバイトシーケンスの位置を印刷する方法を知っていますか?または、誰かが違法なバイトシーケンスを知っていますか?


2
不正なバイトシーケンスは、8ビットASCIIをutf-8を期待するものにフィードするときに得られるもののように聞こえます。
KlasLindbäck2013年

36
あなたは試すことができます:LC_CTYPE=C && LANG=C && sed command
anubhava 2013年

5
皆さんありがとう。それはLANG事だった。ため息....
jww 2013年

3
@ user2719058:BSD sed(OS Xでも使用されます)では-i ''、バックアップファイルなしのインプレース更新に(個別の空の文字列オプション引数)が必要です。GNUとsed、唯一-iそれだけで動作します-参照stackoverflow.com/a/40777793/45375
mklement0

1
それにLANGの1つ。良い悲しみ、それはあいまいで、明白ではなく、研究するのは驚くほど難しいです。
Spudley

回答:


300

sed 's/./@/' <<<$'\xfc'バイト0xfcが有効なUTF-8文字ではないため、症状を示すサンプルコマンドは失敗します。
対照的に、GNU sed(Linux、macOSにもインストール可能)は、エラーを報告せずに、無効なバイトを単に通過させることに注意してください。

以前に受け入れられた回答の使用は、真のロケールのサポートを失うことを気にしない場合のオプションです(米国のシステムを使用していて、外国の文字を処理する必要がない場合は、問題ないかもしれません)。

ただし、同じ効果は、1つのコマンドに対してのみアドホックにできます

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。したがって、最も堅牢なアプローチはを設定することLC_ALLです。

しかし、(効果)の設定LC_CTYPEC扱い、文字列の各バイトは、独自の文字であるかのように何もして、エンコードルールに基づいて解釈が実行されません)なしについて -マルチバイト・オン・デマンド-のためのUTF-8エンコーディング OS Xはデフォルトで採用していることここで、外部文字にマルチバイトエンコーディングが使用されます。

一言で言えば、シェルとユーティリティが基本的な英語の文字(7ビットASCII範囲の文字)のみを認識するように設定LC_CTYPEするCと、外部の文字になります。は文字として扱われないため、たとえば、大文字と小文字の変換が失敗します。

この場合も、などのマルチバイトでエンコードされた文字を一致させる必要がなくé、単にそのような文字をに通したい場合は、これで問題ありません。

これで不十分な場合や、元のエラーの原因(問題の原因となった入力バイトの特定など)を理解し、必要に応じてエンコード変換実行する場合は、以下をお読みください


問題は、入力ファイルのエンコーディングがシェルのエンコーディングと一致しないことです。
より具体的には、入力ファイルには、UTF-8では無効な方法でエンコードされた文字が含まれています(@KlasLindbäckがコメントで述べているように)。これsedが、エラーメッセージがによって伝えようとしていることinvalid byte sequenceです。

ほとんどの場合、入力ファイルは、「西ヨーロッパ」言語のエンコードに頻繁に使用されるなどのシングルバイト8ビットエンコーディングをISO-8859-1使用します。

例:

アクセント付きの文字にàはUnicodeコードポイント0xE0(224)が含まれていISO-8859-1ます-と同じです。ただし、UTF-8エンコーディングの性質上、この単一のコードポイントは2バイト- として表されますが、UTF-8では単一バイト0xC3 0xA0を渡そうとすることは無効です。 0xE0

ここだ問題のデモの文字列を使用してvoilàのようにエンコードISO-8859-1して、àとして表さ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範囲を超えるバイト)が16進数形式で表示されます。(ただし、これには正しくエンコードされたUTF-8マルチバイトシーケンスも含まれることに注意してください。UTF-8で無効なバイトを明確に識別するには、より洗練されたアプローチが必要になります。)


オンデマンドでエンコード変換を実行する

標準ユーティリティiconvを使用して、(-t)および/または(-f)エンコーディングに変換できます。iconv -lサポートされているものをすべてリストします。

例:

上記の例にISO-8859-1基づいてLC_CTYPE、FROM をシェルで有効なエンコーディング(UTF-8デフォルトでは-basedに基づく)に変換します。

  # 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

4
これははるかに良いオプションだと思います。まず、私はすべてのターミナルで多言語サポートを失いたくありません。第二に、受け入れられた答えは、ローカルな問題に対するグローバルな解決策のように感じられます-回避すべき何か。
アレックス

私はこれにいくつかの小さな調整をしました。フィードバックをいただければ幸いです。stackoverflow.com/a/35046218/9636
ヒースボーダーズ

LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'sed: RE error: illegal byte sequenceシエラで私のために版画。FWIWをecho $LC_ALL出力しen_US.UTF-8ます。
ahcox

1
@ahcox:はい。設定は、回答で説明されているように、を含む他のすべての変数をLC_ALL 上書きするためです。LC_*LC_CTYPE
mklement0

2
@ mklement0クール、これは機能します: "LC_ALL = C sed 's /.*/&/' <<< $ 'voil \ x {e0}'"。:優先順位は私の仲間の不注意ignoramusesのためにここで説明pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html
ahcox

142

~/.bash_profileまたは~/.zshrcファイルに次の行を追加します。

export LC_CTYPE=C 
export LANG=C

29
実際に機能しますが、その理由を説明していただけますか?
Hoang Pham 14

11
@HoangPham:に設定LC_CTYPEするCと、エンコードルールを適用せずに、文字列の各バイトが独自の文字になります。(UTF-8)エンコーディングルールに違反すると元の問題が発生したため、これにより問題は解消されます。ただし、支払う価格は、シェルとユーティリティが基本的な英語の文字(7ビットASCII範囲の文字)のみを文字として認識することです。詳細については私の答えを参照してください。
mklement0 2014年

6
シェルのスタートアップファイルでこれを永続的に設定すると、多くの有用な動作が無効になります。これを絶対に必要とする個々のコマンドに対してのみ配置したい。
Tripleee、2016年

4
危険すぎると、予期しない結果が生じる可能性があります。LC_CTYPE=C sed …つまり、sedコマンドでのみ使用できます。
Yongwei Wu 2018

2
これにより、シェルでのUnicode文字のサポートが完全に無効になります。さようならの絵文字、派手な線画文字、アクセント付きの文字など。他の回答で説明されているように、これをsedコマンドのみに設定することをお勧めします。
asmeurer

6

私の回避策はPerlを使用していた:

find . -type f -print0 | xargs -0 perl -pi -e 's/was/now/g'

これは素晴らしい作品です。そして、私は他のものとは異なり、特殊文字をエスケープするエラーがありませんでした。以前のものは、「sed:REエラー:不正なバイトシーケンス」またはsed:1:「path_to_file」:無効なコマンドコードのような問題を私に与えました。
JMags1632

3

mklement0の答えは素晴らしいですが、小さな調整がいくつかあります。

bashを使用する場合は、のエンコーディングを明示的に指定することをお勧めしますiconv。また、バイトオーダーマークがないとUTF-8とASCIIの間で正当な混乱が生じる可能性があるため、(Unicode標準では推奨されていませんがバイトオーダーマークを付加する必要があります。残念ながら、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

1
++役立つテクニック、特にfile -b --mime-encodingファイルのエンコーディングの発見と報告に役立ちます。ただし、対処する価値のあるいくつかの側面がありますが、これについては別のコメントで説明します。
mklement0 2016年

2
デフォルト:私はそれはUnixの世界は、この時点でUTF-8を採用したことを言っても安全だと思うLC_CTYPE値が通常である<lang_region>.UTF-8ので、任意のファイルなしの BOM(バイトオーダーマークが)ので、UTF-8のファイルとして解釈されます。疑似BOMが使用されるのは、Windowsの世界でのみです。定義により、UTF-8はBOMを必要とせず、(あなたが述べるように)推奨されません。Windowsの世界の外に、この疑似BOMは物事がする原因と壊れます 0xef 0xbb 0xff
mklement0 2016年

2
Re Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE):これは設計によるものです。エンディアンを明示的に指定した場合、BOMを介してエンディアンを反映する必要がないため、何も追加されません。
mklement0

1
日時LC_*/ LANG変数:bashksh、およびzsh(おそらく他の人が、ではない dash)文字エンコーディングを尊重します。UTF-8ベースのロケールを持つPOSIXのようなシェルで以下を確認しますv='ä'; echo "${#v}"。UTF-8対応のシェルは報告する必要があり1ます。つまり、マルチバイトシーケンスä0xc3 0xa4)を単一の文字として認識します。おそらくもっと重要なのは、しかし:標準のユーティリティはsedawkcut、...)にも対応のエンコード/ロケールである必要があり、一方で最も近代的なUnixライクなプラットフォーム上でそれらのは、のような例外があるされawk、OSX上では、そしてcutLinux上で。
mklement0

1
fileUTF-8疑似BOM を認識できることは称賛に値しますが、問題は、ファイルを処理するほとんどのUnixユーティリティが認識せ、通常、ファイルに直面したときに壊れるか、少なくとも正しく動作しないことです。BOMがない場合、fileすべての7ビットバイトファイルをASCIIとして正しく識別し、有効なUTF-8マルチバイト文字を持つファイルをUTF-8として正しく識別します。UTF-8の優れた点は、ASCIIのスーパーセットであることです。有効なASCIIファイルはすべて、定義上有効なUTF-8ファイルです(ただし、その逆はありません)。それは(それだけで何のマルチバイト文字が含まれていないために起こる、それは技術的である。)UTF-8としてASCIIファイルを処理するために、安全に完璧だ
mklement0

2

sedコマンドの前にiconvコマンドをパイプするだけです。ex。file.txt入力:

iconv -f ISO-8859-1 -t UTF8-MAC file.txt | sed 's / something /àéèêçùû/ g' | .....

-fオプションは「from」コードセットで、-tオプションは「to」コードセット変換です。

大文字と小文字を区別してください。通常、Webページは<charset = iso-8859-1 "/>のように小文字で表示され、iconvは大文字を使用します。システムでiconvがサポートされているコードセットのリストは、コマンドiconv -lで表示されます。

UTF8-MACは、変換用の最新のOS Macコードセットです。


iconvメーリングリストのiconvおよびcharset名も参照してください。
jww

1

不正なバイトシーケンスの位置を出力するためにsedを取得する方法を知っている人はいますか?または、誰かが違法なバイトシーケンスを知っていますか?

$ 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

次に、正しいバイトコードが何であれ、374をtrに置き換えるようにしようと思うかもしれません。だから私は最初に、うまくいかなかった単純なものを試しましたが、問題のあるバイトがどこにあるかを示すという副作用がありました:

$ 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

0

私の回避策はgnuを使用していたsed。私の目的のためにうまくいきました。


実際、GNU sedは、入力ストリーム内の無効なバイトを無視したい場合(LC_ALL=C sed ...回避策は不要)のオプションです。GNUはエラーを報告する代わりに無効なバイトを渡すsedだけです、すべてを正しく認識して処理したい場合は、入力文字列の文字の場合、最初に入力のエンコーディングを変更する方法はありません(通常、を使用iconv)。
mklement0 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.