regex - Error RE: secuencia de bytes ilegales en Mac OS X
macos bash (5)
Estoy tratando de reemplazar una cadena en un Makefile en Mac OS X para la compilació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?
Agregue las siguientes líneas a su ~/.bash_profile
o ~/.zshrc
archivo (s).
export LC_CTYPE=C
export LANG=C
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.
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 codificación), sin tener en cuenta la codificación - 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 codificación bajo demanda, lea a continuación.
El problema es que la codificación 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 codificación 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 codificación 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 codificación 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 codificación 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
La respuesta de mklement0 es genial, pero tengo algunos pequeños ajustes.
Parece una buena idea especificar explícitamente la codificación 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