Error RE: secuencia de bytes ilegales en Mac OS X

Estoy tratando de reemplazar una cadena en un Makefile en Mac OS X para la comstackción cruzada de iOS. La cadena tiene comillas dobles incrustadas. El comando es:

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

Y el error es:

 sed: RE error: illegal byte sequence 

Intenté escapar de las comillas dobles, comas, guiones y dos puntos sin alegría. Por ejemplo:

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

Estoy teniendo un gran problema para depurar el problema. ¿Alguien sabe cómo obtener sed para imprimir la posición de la secuencia de bytes ilegales? ¿O alguien sabe cuál es la secuencia de bytes ilegales?

Un comando de muestra que muestra el síntoma: sed 's/./@/' <<<$'\xfc' falla, porque el byte 0xfc no es un carácter UTF-8 válido.
Tenga en cuenta que, por el contrario, GNU sed (Linux, pero también instalable en macOS) simplemente pasa el byte inválido, sin informar un error.

Usar la respuesta aceptada anteriormente es una opción si no te importa perder soporte para tu locale verdadera (si estás en un sistema de los EE. UU. Y nunca necesitas tratar con caracteres extranjeros, eso puede estar bien).

Sin embargo, el mismo efecto se puede tener ad-hoc para un solo comando :

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

Nota: Lo que importa es una configuración LC_CTYPE efectiva de C , por lo que LC_CTYPE=C sed ... normalmente también funcionaría, pero si LC_ALL pasa a estar configurado (en algo distinto de C ), anulará las variables de categoría LC_* individuales tales como LC_CTYPE . Por lo tanto, el enfoque más sólido es establecer LC_ALL .

Sin embargo, (efectivamente) establecer LC_CTYPE a C trata cadenas como si cada byte fuera su propio carácter ( no se realiza interpretación basada en reglas de encoding), sin tener en cuenta la encoding - multibyte-on-demand - UTF-8 que OS X emplea de forma predeterminada, donde los caracteres extranjeros tienen codificaciones multibyte .

En pocas palabras: si se establece LC_CTYPE en C , el intérprete de comandos y las utilidades solo reconocerán las letras básicas en inglés como letras (las que están en el rango ASCII de 7 bits), de modo que los caracteres en caracteres extraños. no se tratarán como letras , lo que provocará, por ejemplo, que las conversiones en mayúsculas / minúsculas fallen.

De nuevo, esto puede estar bien si no necesita coincidir con los caracteres codificados en varios bytes, como é , y simplemente desea pasar esos caracteres .

Si esto es insuficiente y / o desea comprender la causa del error original (incluida la determinación de qué bytes de entrada causaron el problema) y realizar conversiones de encoding bajo demanda, lea a continuación.


El problema es que la encoding del archivo de entrada no coincide con la del shell.
Más específicamente, el archivo de entrada contiene caracteres codificados de una manera que no es válida en UTF-8 (como dijo @Klas Lindbäck en un comentario) - eso es lo que el mensaje de error sed intenta decir por una invalid byte sequence .

Lo más probable es que su archivo de entrada utilice una encoding de 8 bytes de un solo byte , como ISO-8859-1 , que se utiliza con frecuencia para codificar idiomas de "Europa occidental".

Ejemplo:

La letra a acentuada tiene un punto de código Unicode 0xE0 (224), el mismo que en ISO-8859-1 . Sin embargo, debido a la naturaleza de la encoding UTF-8 , este único punto de código se representa como 2 bytes - 0xC3 0xA0 , mientras que intentar pasar el byte único 0xE0 no es válido bajo UTF-8.

Aquí hay una demostración del problema usando la cadena voilà codificada como ISO-8859-1 , con el à representado como un byte (a través de una cadena bash ( $'...' ) que usa \x{e0} para crear el byte):

Tenga en cuenta que el comando sed es efectivamente un no-op que simplemente pasa la entrada, pero necesitamos que provoque el error:

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

Para simplemente ignorar el problema , se puede usar el enfoque LCTYPE=C anterior:

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

Si desea determinar qué partes de la entrada causan el problema , intente lo siguiente:

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

La salida le mostrará todos los bytes que tienen el bit alto establecido (bytes que superan el rango ASCII de 7 bits) en forma hexadecimal. (Sin embargo, tenga en cuenta que eso también incluye secuencias de multibyte UTF-8 codificadas correctamente; sería necesario un enfoque más sofisticado para identificar específicamente los bytes inválidos en UTF-8).


Realización de conversiones de encoding bajo demanda :

El iconv utilidad estándarv se puede usar para convertir a codificaciones ( -t ) y / o ( -f ); iconv -l enumera todos los compatibles.

Ejemplos:

Convierta de ISO-8859-1 a la encoding vigente en el shell (basada en LC_CTYPE , que está basada en UTF-8 de manera predeterminada), basándose en el ejemplo anterior:

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

Tenga en cuenta que esta conversión le permite emparejar correctamente caracteres extraños :

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

Para convertir la entrada BACK a ISO-8859-1 después del procesamiento, simplemente canalice el resultado a otro comando iconv :

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

Agregue las siguientes líneas a su ~/.bash_profile o ~/.zshrc archivo (s).

 export LC_CTYPE=C export LANG=C 

La respuesta de mklement0 es genial, pero tengo algunos pequeños ajustes.

Parece una buena idea especificar explícitamente la encoding de bash cuando se usa iconv . Además, debemos anteponer una marca de orden de bytes ( aunque el estándar Unicode no lo recomiende ) porque puede haber confusiones legítimas entre UTF-8 y ASCII sin una marca de orden de bytes . Desafortunadamente, iconv no antecede una marca de orden de bytes cuando especifica explícitamente una endianidad ( UTF-16BE o UTF-16LE ), por lo que debemos usar UTF-16 , que utiliza endianidad específica de la plataforma, y ​​luego usar el file --mime-encoding para descubrir el iconv endianness verdadero utilizado.

(Copio mayúsculas todas mis codificaciones porque cuando iconv todas las codificaciones soportadas de iconv -l con iconv -l todas están en mayúsculas).

 # 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 

Mi solución ha sido usar Perl:

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

Mi solución había sido usar gnu sed . Funcionó bien para mis propósitos.