Errore RE: sequenza di byte illegale su Mac OS X.


184

Sto cercando di sostituire una stringa in un Makefile su Mac OS X per la compilazione incrociata su iOS. La stringa ha doppie virgolette incorporate. 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 alle doppie virgolette, virgole, trattini e due punti senza gioia. Per esempio:

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

Sto avendo un sacco di tempo il debug del problema. Qualcuno sa come sedstampare la posizione della sequenza di byte illegale? O qualcuno sa quale sia la sequenza di byte illegali?


2
La sequenza di byte illegali sembra qualcosa che ottieni quando dai a ascii a 8 bit qualcosa che si aspetta utf-8.
Klas Lindbäck,

36
Puoi provare:LC_CTYPE=C && LANG=C && sed command
anubhava l'

5
Grazie gente. Era la LANGcosa. Sospiro ....
jww

3
@ user2719058: BSD sed(utilizzato anche su OS X) richiede -i ''(argomento opzione separato, stringa vuota) per l'aggiornamento sul posto senza un file di backup; con GNU sed, solo -iper sé funziona - vedi stackoverflow.com/a/40777793/45375
mklement0

1
Più uno per la cosa LANG. Buon dolore, è oscuro, non ovvio e sorprendentemente difficile da ricercare.
Spudley,

Risposte:


300

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

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

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

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

Nota: Ciò che conta è una efficace LC_CTYPE impostazione C, quindi LC_CTYPE=C sed ...sarebbe normalmente anche il lavoro, ma se LC_ALLcapita di essere impostato (a qualcosa di diverso C), avrà la precedenza singole LC_*variabili -Categoria quali LC_CTYPE. Pertanto, l'approccio più solido è quello di impostare LC_ALL.

Tuttavia, l'impostazione (efficacemente) LC_CTYPEdi Ctrattare le stringhe come se ogni byte fosse il proprio carattere ( non viene eseguita alcuna interpretazione basata sulle regole di codifica), senza tener conto della codifica UTF-8 multibyte su richiesta che OS X utilizza per impostazione predefinita , in cui i caratteri stranieri hanno codifiche multibyte .

In breve: l' impostazione LC_CTYPEinC modo che la shell e le utility riconoscano solo le lettere inglesi di base come lettere (quelle nell'intervallo ASCII a 7 bit), in modo che i caratteri stranieri. non saranno trattati come lettere , causando, ad esempio, conversioni maiuscole / minuscole.

Ancora una volta, questo può andare bene se non è necessario abbinare caratteri con codifica multibyte come ée se si desidera semplicemente passare tali caratteri attraverso .

Se questo è insufficiente e / o si desidera comprendere la causa dell'errore originale (inclusa la determinazione di quali byte di input hanno causato il problema) ed eseguire conversioni di codifica su richiesta, leggere di seguito.


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 che non è valido in UTF-8 (come affermato da @Klas Lindbäck in un commento) - questo è ciò che il sedmessaggio di errore sta cercando di dire invalid byte sequence.

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

Esempio:

La lettera accentata àha un punto di codice Unicode 0xE0(224) - lo stesso di in 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 non0xE0 è valido in UTF-8.

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

Si noti che il sedcomando è effettivamente una no-op che passa semplicemente attraverso 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 , è LCTYPE=Cpossibile utilizzare l'approccio sopra :

  # 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 alto impostato (byte che superano l'intervallo ASCII a 7 bit) in forma esadecimale. (Si noti, tuttavia, che include anche sequenze multibyte UTF-8 correttamente codificate: sarebbe necessario un approccio più sofisticato per identificare specificamente i byte non validi in UTF-8.)


Esecuzione della codifica delle conversioni su richiesta :

L'utilità standard iconvpuò essere utilizzata per convertire in ( -t) e / o da ( -f) codifiche;iconv -lelenca tutti quelli supportati.

Esempi:

Converti FROM ISO-8859-1nella codifica attiva nella shell (basata su LC_CTYPE, che è UTF-8basata di default), basandosi sull'esempio sopra:

  # 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-1dopo l'elaborazione, è sufficiente reindirizzare il risultato a un altro iconvcomando:

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

4
Direi che questa è un'opzione molto migliore. Innanzitutto, non vorrei perdere il supporto multilingue in tutto Terminal. In secondo luogo, la risposta accettata sembra una soluzione globale a un problema locale - qualcosa da evitare.
Alex,

Ho avuto un paio di piccole modifiche a questo. Gradirei feedback. stackoverflow.com/a/35046218/9636
Heath Borders,

LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'stampe sed: RE error: illegal byte sequenceper me sulla Sierra. echo $LC_ALLuscite en_US.UTF-8FWIW.
ahcox,

1
@ahcox: Sì, perché l'impostazione LC_ALL sovrascrive tutte le altre LC_*variabili, incluso LC_CTYPE, come spiegato nella risposta.
mklement0,

2
@ mklement0 Bene, questo funziona: "LC_ALL = C sed 's /.*/&/' <<< $ 'voil \ x {e0}'". Precedenza spiegata qui per i miei compagni ignoranti disattenti: pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html
ahcox

142

Aggiungi le seguenti righe al tuo ~/.bash_profileo ai tuoi ~/.zshrcfile.

export LC_CTYPE=C 
export LANG=C

29
funziona davvero, ma potresti spiegare perché?
Hoang Pham,

11
@HoangPham: l'impostazione LC_CTYPEsu Cfa sì che ogni byte nelle stringhe sia il proprio carattere senza applicare alcuna regola di codifica. Poiché una violazione delle regole di codifica (UTF-8) ha causato il problema originale, questo risolve il problema. Tuttavia, il prezzo da pagare è che la shell e le utility riconoscano solo le lettere inglesi di base (quelle nell'intervallo ASCII a 7 bit) come lettere. Vedi la mia risposta per di più.
mklement0

6
L'impostazione permanente di questo nei file di avvio della shell disabiliterà molti comportamenti utili. Si desidera inserire questo solo per i singoli comandi che lo richiedono assolutamente.
triplo il

4
Troppo pericoloso può causare conseguenze inaspettate. Si potrebbe usare LC_CTYPE=C sed …, cioè solo sul comando sed.
Yongwei Wu

2
Ciò disabiliterà completamente il supporto per i caratteri Unicode nella shell. Addio emoji, fantasiosi disegni a tratteggio, lettere con accenti, ... Molto meglio impostare questo solo per il comando sed, come descritto in altre risposte.
asmeurer

6

La mia soluzione alternativa stava usando Perl:

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

Questo funziona alla grande. E non ho avuto errori sfuggendo a personaggi speciali a differenza degli altri. I precedenti mi davano problemi come "sed: RE errore: sequenza di byte illegale" o sed: 1: "path_to_file": codice comando non valido.
JMags1632

3

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

Sembra una buona idea specificare esplicitamente bashla codifica durante l'utilizzo iconv. Inoltre, dovremmo anteporre un segno di ordine di byte ( anche se lo standard unicode non lo raccomanda ) perché possono esserci confusioni legittime tra UTF-8 e ASCII senza un segno di ordine di byte . Sfortunatamente, iconvnon anteporre un segno di ordine di byte quando si specifica esplicitamente un endianness ( UTF-16BEo UTF-16LE), quindi è necessario utilizzare UTF-16, che utilizza endianness specifico della piattaforma, quindi utilizzare file --mime-encodingper scoprire il vero endianness iconvutilizzato.

(Metto in maiuscolo tutte le mie codifiche perché quando elenchi tutte iconvle codifiche supportate con iconv -lsono tutte in maiuscolo.)

# 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
++ per tecniche utili, in particolare file -b --mime-encodingper scoprire e segnalare la codifica di un file. Ci sono alcuni aspetti che vale la pena affrontare, tuttavia, che farò in commenti separati.
mklement0,

2
Penso che sia sicuro affermare che il mondo Unix ha abbracciato UTF-8 a questo punto: il LC_CTYPEvalore predefinito è di solito <lang_region>.UTF-8, quindi qualsiasi file senza BOM (segno di ordine byte) viene quindi interpretato come un file UTF-8. È solo nel mondo Windows che viene utilizzata la pseudo-distinta base 0xef 0xbb 0xff ; per definizione, UTF-8 non ha bisogno di una distinta base e non è raccomandato (come indicato); al di fuori del mondo di Windows, questa pseudo-distinta componenti causa la rottura delle cose .
mklement0

2
Ri Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE): questo è in base alla progettazione: se si specifica esplicitamente l'endianness , non è necessario rifletterlo anche tramite una DBA, quindi non ne viene aggiunto nessuno.
mklement0,

1
Re LC_*/ LANGvariabili: bash, ksh, e zsh(possibilmente altri, ma non dash ) fare rispettare la codifica dei caratteri; verifica in shell tipo POSIX con impostazioni internazionali basate su UTF-8 con v='ä'; echo "${#v}": una shell con consapevolezza UTF-8 dovrebbe riportare 1; cioè, dovrebbe riconoscere la sequenza multi-byte ä( 0xc3 0xa4), come un singolo carattere. Forse ancora più importante, però: le utilità standard ( sed, awk, cut, ...) devono anche essere locale / codifica-aware, e mentre la maggior parte di loro su moderne piattaforme Unix-like sono, ci sono delle eccezioni, come ad esempio awksu OSX, e cutsu Linux.
mklement0

1
È encomiabile che filericonosce la pseudo-distinta base UTF-8, ma il problema è che la maggior parte dei programmi di utilità Unix che elaborano i file non lo fanno e di solito si rompono o si comportano in modo anomalo di fronte a uno. Senza una distinta componenti, fileidentifica correttamente un file di byte a 7 bit come ASCII e uno con caratteri multi-byte UTF-8 validi come UTF-8. Il bello di UTF-8 è che è un superset di ASCII: qualsiasi file ASCII valido è per definizione un file UTF-8 valido (ma non viceversa); è perfettamente sicuro trattare un file ASCII come UTF-8 (che tecnicamente è, non sembra contenere caratteri multibyte.)
mklement0

2

Devi semplicemente reindirizzare un comando iconv prima del comando sed . Es. Con input file.txt:

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

L' opzione -f è il set di codici "da" e l'opzione -t è la conversione del set di codici "in".

Prenditi cura del caso, le pagine web di solito mostrano lettere minuscole come quelle <charset = iso-8859-1 "/> e iconv usa lettere maiuscole. Hai un elenco di codici supportati da iconv nel tuo sistema con il comando iconv -l

UTF8-MAC è un moderno set di codici per Mac OS per la conversione.


Vedi anche i nomi iconv e charset nella mailing list iconv.
jww

1

Qualcuno sa come ottenere sed per stampare la posizione della sequenza di byte illegale? O qualcuno sa qual è la sequenza di byte illegali?

$ 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

Ho avuto modo di rispondere a quanto sopra semplicemente usando tr .

Ho un file .csv che è un estratto conto della carta di credito e sto provando a importarlo in Gnucash. Sono residente in Svizzera, quindi devo occuparmi di parole come Zurigo. Sospettare che Gnucash non piaccia "" nei campi numerici, decido semplicemente di sostituire tutto

; ;

con

;;

Ecco qui:

$ head -3 Auswertungen.csv | tail -1 | sed -e 's/; ;/;;/g'
sed: RE error: illegal byte sequence

Ho usato od per far luce: nota il 374 a metà di questa uscita od -c

$ 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

Poi ho pensato di provare a convincere tr a sostituire 374 con qualunque sia il codice byte corretto. Quindi prima ho provato qualcosa di semplice, che non ha funzionato, ma ha avuto l'effetto collaterale di mostrarmi dove si trovava il byte problematico:

$ 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

Puoi vedere tr bails al 374 carattere.

L'uso di perl sembra evitare questo problema

$ 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

La mia soluzione alternativa stava usando gnu sed. Ha funzionato bene per i miei scopi.


In effetti, GNU sed è un'opzione se si desidera ignorare i byte non validi nel flusso di input (non è necessaria la LC_ALL=C sed ...soluzione alternativa), poiché GNU passased semplicemente byte non validi invece di segnalare un errore, ma si noti che se si desidera riconoscere ed elaborare correttamente tutti caratteri nella stringa di input, non è possibile modificare prima la codifica dell'input (in genere, con iconv).
mklement0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.