Estoy tratando de reemplazar una cadena en un Makefile en Mac OS X para la compilación cruzada a 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
He intentado 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
Me está costando mucho depurar el problema. ¿Alguien sabe cómo sed
imprimir la posición de la secuencia de bytes ilegal? ¿O alguien sabe cuál es la secuencia ilegal de bytes?
LC_CTYPE=C && LANG=C && sed command
LANG
cosa. Suspiro ....sed
(como también se usa en OS X) requiere-i ''
(argumento de opción de cadena vacía separada) para la actualización in situ sin un archivo de copia de seguridad; con GNUsed
, solo funciona-i
por sí solo - ver stackoverflow.com/a/40777793/45375Respuestas:
Un comando de muestra que muestra el síntoma:
sed 's/./@/' <<<$'\xfc'
falla, porque el byte0xfc
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 no válido, sin informar un error.Usar la respuesta anteriormente aceptada es una opción si no le importa perder el soporte para su ubicación local verdadera (si está en un sistema de EE. UU. Y nunca necesita tratar con caracteres extranjeros, puede estar bien).
Sin embargo, el mismo efecto se puede conseguir ad-hoc para un solo comando solamente :
Nota: Lo que importa es una configuración efectiva
LC_CTYPE
deC
, porLC_CTYPE=C sed ...
lo que normalmente también funcionaría, pero siLC_ALL
se establece (en algo diferente aC
), anulará lasLC_*
variables de categoría individual comoLC_CTYPE
. Por lo tanto, el enfoque más robusto es establecerLC_ALL
.Sin embargo, (efectivamente) se configura
LC_CTYPE
paraC
tratar las cadenas como si cada byte fuera su propio carácter ( no se realiza ninguna interpretación basada en reglas de codificación), sin tener en cuenta la codificación UTF-8 - multibyte-on-demand - que OS X emplea de manera predeterminada , donde los caracteres extranjeros tienen codificaciones multibyte .En pocas palabras: establecer
LC_CTYPE
enC
hace que el shell y las utilidades solo reconozcan letras inglesas básicas como letras (las que están en el rango ASCII de 7 bits), por lo que los caracteres extranjeros. no se tratarán como letras , lo que hará que, por ejemplo, las conversiones en mayúsculas / minúsculas fallen.Una vez más, esto puede estar bien si no necesita hacer coincidir caracteres codificados con varios bytes como
é
, y simplemente desea pasar dichos 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 a pedido, 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
sed
mensaje de error intenta decirinvalid byte sequence
.Lo más probable es que su archivo de entrada utilice una codificación de 8 bits de un solo byte , como la que
ISO-8859-1
se usa con frecuencia para codificar idiomas "europeos occidentales".Ejemplo:
La letra acentuada
à
tiene un punto de código Unicode0xE0
(224), lo mismo que enISO-8859-1
. Sin embargo, debido a la naturaleza de la codificación UTF-8 , este único punto de código se representa como 2 bytes0xC3 0xA0
, mientras que intentar pasar el byte único no0xE0
es válido bajo UTF-8.Aquí hay una demostración del problema usando la cadena
voilà
codificada comoISO-8859-1
, con laà
representada como un byte (a través de una cadena bash ($'...'
) citada por ANSI-C que se usa\x{e0}
para crear el byte):Tenga en cuenta que el
sed
comando es efectivamente un no-op que simplemente pasa la entrada, pero lo necesitamos para provocar el error:Para simplemente ignorar el problema ,
LCTYPE=C
se puede usar el enfoque anterior :Si desea determinar qué partes de la entrada causan el problema , intente lo siguiente:
La salida le mostrará todos los bytes que tienen el conjunto de bits alto (bytes que exceden el rango ASCII de 7 bits) en forma hexadecimal. (Sin embargo, tenga en cuenta que eso también incluye secuencias multibyte UTF-8 codificadas correctamente: se necesitaría un enfoque más sofisticado para identificar específicamente bytes no válidos en UTF-8).
Realización de conversiones de codificación bajo demanda :
La utilidad estándar
iconv
se puede usar para convertir a (-t
) y / o desde (-f
) codificaciones;iconv -l
enumera todos los compatibles.Ejemplos:
Convierta FROM
ISO-8859-1
a la codificación vigente en el shell (basado enLC_CTYPE
, que estáUTF-8
basado por defecto), basándose en el ejemplo anterior:Tenga en cuenta que esta conversión le permite hacer coincidir correctamente los caracteres extranjeros :
Para convertir la entrada BACK a
ISO-8859-1
después del procesamiento, simplemente canalice el resultado a otroiconv
comando:fuente
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
impresionessed: RE error: illegal byte sequence
para mí en Sierra.echo $LC_ALL
salidasen_US.UTF-8
FWIW.LC_ALL
anula todas las demásLC_*
variables, incluidaLC_CTYPE
, como se explica en la respuesta.Agregue las siguientes líneas a su
~/.bash_profile
o~/.zshrc
archivo (s).fuente
LC_CTYPE
enC
hace que cada byte en cadenas sea su propio carácter sin aplicar ninguna regla de codificación. Dado que una violación de las reglas de codificación (UTF-8) causó el problema original, esto hace que el problema desaparezca. Sin embargo, el precio que paga es que el shell y las utilidades solo reconocen las letras inglesas básicas (las que están en el rango ASCII de 7 bits) como letras. Vea mi respuesta para más.LC_CTYPE=C sed …
, es decir, solo en el comando sed.Mi solución había estado usando Perl:
fuente
La respuesta de mklement0 es genial, pero tengo algunos pequeños ajustes.
Parece una buena idea especificar explícitamente
bash
la codificación cuando se usaiconv
. Además, deberíamos anteponer una marca de orden de bytes ( aunque el estándar Unicode no lo recomienda ) porque puede haber confusiones legítimas entre UTF-8 y ASCII sin una marca de orden de bytes . Desafortunadamente,iconv
no antepone una marca de orden de bytes cuando especifica explícitamente una endianness (UTF-16BE
oUTF-16LE
), por lo que debemos usarUTF-16
, que usa endianness específico de la plataforma, y luego usarfile --mime-encoding
para descubrir la verdadera endiannessiconv
utilizada.(En mayúscula todas mis codificaciones porque cuando enumeras todas
iconv
las codificaciones admitidasiconv -l
, todas son mayúsculas).fuente
file -b --mime-encoding
para descubrir y reportar la codificación de un archivo. Sin embargo, hay algunos aspectos que vale la pena abordar, que haré en comentarios separados.LC_CTYPE
valor predeterminado suele ser<lang_region>.UTF-8
, por lo que cualquier archivo sin BOM (marca de orden de bytes) se interpreta como un archivo UTF-8. Solo en el mundo de Windows se usa el pseudo-BOM0xef 0xbb 0xff
; por definición, UTF-8 no necesita una lista de materiales y no se recomienda (como usted dice ); fuera del mundo de Windows, esta pseudo-BOM hace que las cosas se rompan .Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE)
: eso es por diseño: si especificas el endianness explícitamente , no hay necesidad de reflejarlo también a través de una lista de materiales, por lo que no se agrega ninguno.LC_*
/LANG
variables:bash
,ksh
, yzsh
(posiblemente otros, pero nodash
) hacer respetar la codificación de caracteres; verificar en shells tipo POSIX con un entorno local basado en UTF-8 conv='ä'; echo "${#v}"
: un shell con reconocimiento UTF-8 debe informar1
; es decir, debe reconocer la secuencia de varios bytesä
(0xc3 0xa4
), como un solo carácter. Tal vez aún más importante, sin embargo: las utilidades estándar (sed
,awk
,cut
, ...) también tienen que ser locale / codificación-consciente, y si bien la mayoría de ellos en la moderna Unix plataformas son, hay excepciones, comoawk
en OSX, ycut
en Linux.file
reconozca el pseudo-BOM UTF-8, pero el problema es que la mayoría de las utilidades de Unix que procesan el archivo no lo hacen , y generalmente se rompen o al menos se comportan mal cuando se enfrentan con uno. Sin una lista de materiales,file
identifica correctamente un archivo de bytes de todos los 7 bits como ASCII, y uno que tiene caracteres válidos de múltiples bytes UTF-8 como UTF-8. La belleza de UTF-8 es que es un superconjunto de ASCII: cualquier archivo ASCII válido es, por definición, un archivo UTF-8 válido (pero no al revés); es perfectamente seguro tratar un archivo ASCII como UTF-8 (que técnicamente es, simplemente no contiene caracteres de varios bytes)Simplemente tiene que canalizar un comando iconv antes del comando sed . Ej con entrada file.txt:
-F opción es el conjunto de códigos 'desde' y la opción -t es la conversión del conjunto de códigos 'a'.
Tenga cuidado con las mayúsculas y minúsculas, las páginas web generalmente muestran minúsculas como <charset = iso-8859-1 "/> e iconv usa mayúsculas. Tiene una lista de conjuntos de códigos compatibles iconv en su sistema con el comando iconv -l
UTF8-MAC es un moderno conjunto de códigos OS Mac para la conversión.
fuente
Obtuve parte del camino para responder lo anterior simplemente usando tr .
Tengo un archivo .csv que es un extracto de la tarjeta de crédito y estoy tratando de importarlo a Gnucash. Estoy basado en Suiza, así que tengo que lidiar con palabras como Zürich. Sospechando que a Gnucash no le gusta "" en los campos numéricos, decido simplemente reemplazar todos
con
Aquí va:
Solía od para arrojar algo de luz: tenga en cuenta el 374 a la mitad de esta salida od -c
Entonces pensé que podría tratar de persuadir a tr para que sustituya 374 por el código de byte correcto. Así que primero probé algo simple, que no funcionó, pero tuvo el efecto secundario de mostrarme dónde estaba el byte problemático:
Puedes ver tr fianzas en el carácter 374.
Usar perl parece evitar este problema
fuente
Mi solución había estado usando GNU
sed
. Funcionó bien para mis propósitos.fuente
sed
es una opción si desea ignorar los bytes no válidos en la secuencia de entrada (sin necesidad de laLC_ALL=C sed ...
solución alternativa), porque GNUsed
simplemente pasa bytes no válidos en lugar de informar un error, pero tenga en cuenta que si desea reconocer y procesar correctamente todos caracteres en la cadena de entrada, no hay forma de cambiar primero la codificación de la entrada (normalmente, coniconv
).