Errore RE: sequenza di byte illegale su Mac OS X

Sto cercando di sostituire una stringa in un Makefile su Mac OS X per eseguire cross-compiling su iOS. La stringa ha incorporato doppie virgolette. Il comando è:

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

E l’errore è:

 sed: RE error: illegal byte sequence 

Ho provato a sfuggire le doppie virgolette, virgole, trattini e due punti senza alcuna gioia. Per esempio:

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

Sto passando un po ‘di tempo a risolvere il problema. Qualcuno sa come ottenere sed per stampare la posizione della sequenza di byte illegale? O qualcuno sa qual è la sequenza di byte illegale?

Un comando di esempio che mostra il sintomo: sed 's/./@/' <<<$'\xfc' fallisce, perché byte 0xfc non è un carattere UTF-8 valido.
Si noti che, al contrario, GNU sed (Linux, ma anche installabile su macOS) passa semplicemente il byte invalido, senza riportare un errore.

L'uso della risposta precedentemente accettata è un'opzione se non ti dispiace perdere il supporto per la tua vera localizzazione (se sei su un sistema americano e non hai mai bisogno di occuparti di caratteri stranieri, potrebbe andare bene.)

Tuttavia, lo stesso effetto può essere avuto ad-hoc solo per un singolo comando :

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

Nota: ciò che conta è un'effettiva impostazione LC_CTYPE di C , quindi LC_CTYPE=C sed ... normalmente funzionerebbe anche, ma se LC_ALL dovesse essere impostato (su qualcosa di diverso da C ), sovrascriverà le LC_* individuali di LC_* -category tali come LC_CTYPE . Pertanto, l'approccio più efficace consiste LC_ALL .

Tuttavia, l'impostazione (efficace) di LC_CTYPE su C considera le stringhe come se ogni byte fosse il proprio carattere ( non viene eseguita alcuna interpretazione basata sulle regole di codifica), senza riguardo per la codifica UTF-8 multibyte su richiesta utilizzata da OS X per impostazione predefinita, dove i caratteri stranieri hanno codifiche multibyte .

In poche parole: l' impostazione di LC_CTYPE su C fa sì che la shell e le utilità riconoscano solo le lettere inglesi di base come lettere (quelle nell'intervallo ASCII a 7 bit), in modo che i caratteri esterni. non saranno trattati come lettere , causando, ad esempio, conversioni maiuscole / minuscole per fallire.

Anche in questo caso, ciò può andare bene se non è necessario abbinare caratteri con codifica multibyte come é , e semplicemente passare tali caratteri .

Se questo è insufficiente e / o vuoi capire la causa dell'errore originale (incluso determinare quali byte di input hanno causato il problema) ed eseguire conversioni di codifica su richiesta, leggi sotto.


Il problema è che la codifica del file di input non corrisponde alla shell.
Più specificamente, il file di input contiene caratteri codificati in un modo non valido in UTF-8 (come dichiarato da @Klas Lindbäck in un commento) - questo è ciò che il messaggio di errore sed sta tentando di pronunciare con una invalid byte sequence .

Molto probabilmente, il tuo file di input utilizza una codifica a 8 bit a singolo byte come ISO-8859-1 , usata frequentemente per codificare le lingue "dell'Europa occidentale".

Esempio:

La lettera accentata à ha il codice Unicode 0xE0 (224) - lo stesso di ISO-8859-1 . Tuttavia, a causa della natura della codifica UTF-8 , questo singolo punto di codice è rappresentato come 2 byte - 0xC3 0xA0 , mentre il tentativo di passare il singolo byte 0xE0 non è valido in UTF-8.

Ecco una dimostrazione del problema usando la stringa voilà codificata come ISO-8859-1 , con il ISO-8859-1 à rappresentato da un byte (tramite una stringa bash citata da ANSI-C ( $'...' ) che usa \x{e0} per creare il byte):

Si noti che il comando sed è effettivamente un no-op che passa semplicemente l'input, ma ne abbiamo bisogno per provocare l'errore:

  # -> 'illegal byte sequence': byte 0xE0 is not a valid char. sed 's/.*/&/' <<<$'voil\x{e0}' 

Per ignorare semplicemente il problema , è ansible utilizzare il precedente approccio LCTYPE=C :

  # No error, bytes are passed through ('á' will render as '?', though). LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}' 

Se si desidera determinare quali parti dell'input causano il problema , provare quanto segue:

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

L'output mostrerà tutti i byte con il bit più alto impostato (byte che superano l'intervallo ASCII a 7 bit) in formato esadecimale. (Nota, tuttavia, che include anche sequenze multibyte UTF-8 codificate correttamente: sarebbe necessario un approccio più sofisticato per identificare specificamente i byte non validi in UTF-8.)


Esecuzione di conversioni di codifica su richiesta :

È ansible utilizzare iconv utilità standard per convertire le codifiche ( -t ) e / o from ( -f ); iconv -l elenca tutti quelli supportati.

Esempi:

Convertire da ISO-8859-1 alla codifica in vigore nella shell (basata su LC_CTYPE , che è basata su UTF-8 per impostazione predefinita), sulla base dell'esempio sopra riportato:

  # Converts to UTF-8; output renders correctly as 'voilà' sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" 

Tieni presente che questa conversione ti consente di abbinare correttamente i caratteri stranieri :

  # Correctly matches 'à' and replaces it with 'ü': -> 'voilü' sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" 

Per convertire l'input BACK in ISO-8859-1 dopo l'elaborazione, è sufficiente colbind il risultato a un altro comando iconv :

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

Aggiungi le seguenti righe ai tuoi file ~/.bash_profile o ~/.zshrc .

 export LC_CTYPE=C export LANG=C 

La risposta di mklement0 è ottima, ma ho alcune piccole modifiche.

Sembra una buona idea specificare esplicitamente la codifica di bash quando si usa iconv . Inoltre, dovremmo anteporre un segno di ordinamento dei byte ( anche se lo standard unicode non lo consiglia ) perché possono esserci confusioni legittime tra UTF-8 e ASCII senza un segno di ordinamento dei byte . Sfortunatamente, iconv non antepone un segno di ordine di byte quando si specifica esplicitamente un endianness ( UTF-16BE o UTF-16LE ), quindi è necessario utilizzare UTF-16 , che utilizza l’endianness specifica della piattaforma e quindi utilizzare il file --mime-encoding per scoprire la vera endianità iconv usata.

(Ho maiuscolo tutte le mie codifiche perché quando si elencano tutte le codifiche supportate da iconv -l con iconv -l sono tutte maiuscole.)

 # 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 

La mia soluzione era stata l’utilizzo di Perl:

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

La mia soluzione era stata l’utilizzo di gnu sed . Ha funzionato bene per i miei scopi.