Erreur RE: séquence d’octets illégale sur Mac OS X

J’essaye de remplacer une chaîne dans un Makefile sur Mac OS X pour la compilation croisée vers iOS. La chaîne contient des guillemets doubles. La commande est la suivante:

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

Et l’erreur est la suivante:

 sed: RE error: illegal byte sequence 

J’ai essayé d’échapper aux guillemets, virgules, tirets et deux-points sans joie. Par exemple:

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

J’ai beaucoup de mal à résoudre le problème. Est-ce que quelqu’un sait comment obtenir pour imprimer la position de la séquence d’octets illégale? Ou quelqu’un sait-il quelle est la séquence d’octets illégale?

Un exemple de commande sed 's/./@/' < <<$'\xfc' le symptôme suivant: sed 's/./@/' < <<$'\xfc' échoue, car l'octet 0xfc n'est pas un caractère UTF-8 valide.
Notez qu'au contraire, GNU sed (Linux, mais aussi installable sur macOS) ne fait que transmettre l'octet invalide sans signaler d'erreur.

L'utilisation de la réponse précédemment acceptée est une option si cela ne vous dérange pas de perdre le support pour vos parameters régionaux réels (si vous êtes sur un système américain et que vous n'avez jamais besoin de traiter des caractères étrangers, cela peut vous convenir).

Toutefois, le même effet peut être ad hoc pour une seule commande uniquement :

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

Note: Ce qui compte, c’est un paramètre LC_CTYPE efficace de C , donc LC_CTYPE=C sed ... fonctionnera normalement aussi, mais si LC_ALL arrive à être défini (à autre chose que C ), il remplacera les LC_* catégories LC_* individuelles telles que comme LC_CTYPE . Ainsi, l'approche la plus robuste consiste à définir LC_ALL .

Cependant, (effectivement) définir LC_CTYPE sur C traite les chaînes comme si chaque octet était son propre caractère ( aucune interprétation basée sur les règles de codage n'est exécutée), sans tenir compte du codage UTF-8 multi-octets à la demande utilisé par OS X par défaut, les caractères étrangers sont codés sur plusieurs octets .

En bref: définir LC_CTYPE sur C permet au shell et aux utilitaires de ne reconnaître que les lettres anglais de base sous forme de lettres (celles de la plage ASCII 7 bits), de sorte que les caractères étrangers. ne seront pas traités comme des lettres , entraînant par exemple l'échec des conversions majuscules / minuscules.

Encore une fois, cela peut être correct si vous n'avez pas besoin de correspondre à des caractères codés sur plusieurs octets, tels que é , et que vous voulez simplement passer de tels caractères .

Si cela est insuffisant et / ou si vous voulez comprendre la cause de l'erreur d'origine (y compris la détermination des octets d'entrée à l'origine du problème) et effectuer des conversions de codage à la demande, lisez la suite.


Le problème est que le codage du fichier d'entrée ne correspond pas à celui du shell.
Plus précisément, le fichier d'entrée contient des caractères codés d'une manière qui n'est pas valide dans UTF-8 (comme @Klas Lindbäck l'a indiqué dans un commentaire) - c'est ce que le message d'erreur sed tente de dire par invalid byte sequence .

Très probablement, votre fichier d’entrée utilise un encodage 8 bits à un octet tel que ISO-8859-1 , fréquemment utilisé pour coder les langues «européennes de l’Ouest».

Exemple:

La lettre accentuée à a le codet Unicode 0xE0 (224) - le même que dans ISO-8859-1 . Cependant, en raison de la nature du codage UTF-8 , ce seul sharepoint code est représenté par 2 octets - 0xC3 0xA0 , alors que tenter de transmettre le seul octet 0xE0 n'est pas valide sous UTF-8.

Voici une démonstration du problème en utilisant la chaîne voilà codée ISO-8859-1 , avec un représenté par un octet (via une chaîne bash citée par ANSI-C ( $'...' ) qui utilise \x{e0} pour créer l'octet):

Notez que la commande sed est en réalité une opération qui passe simplement l’entrée, mais nous en avons besoin pour provoquer l’erreur:

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

Pour simplement ignorer le problème , l'approche LCTYPE=C ci-dessus peut être utilisée:

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

Si vous souhaitez déterminer quelles parties de l'entrée sont à l'origine du problème , procédez comme suit:

  # 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 sortie vous montrera tous les octets ayant le jeu de bits élevé (octets dépassant la plage ASCII 7 bits) sous forme hexadécimale. (Notez toutefois que cela inclut également des séquences multi-octets UTF-8 correctement codées - une approche plus sophistiquée serait nécessaire pour identifier spécifiquement les octets d'invalide dans l'UTF-8.)


Effectuer des conversions d'encodage à la demande :

L'utilitaire standard iconv peut être utilisé pour convertir en encodages ( -t ) et / ou à partir de ( -f ); iconv -l liste tous ceux supportés.

Exemples:

Convertissez FROM ISO-8859-1 en encodage en vigueur dans le shell (basé sur LC_CTYPE , basé sur UTF-8 par défaut), en LC_CTYPE l'exemple ci-dessus:

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

Notez que cette conversion vous permet de faire correspondre correctement les caractères étrangers :

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

Pour convertir l'entrée BACK en ISO-8859-1 après traitement, il suffit de iconv le résultat vers une autre commande iconv :

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

Ajoutez les lignes suivantes à vos fichiers ~/.bash_profile ou ~/.zshrc .

 export LC_CTYPE=C export LANG=C 

La réponse de mklement0 est géniale, mais j’ai quelques petites modifications.

Il semble judicieux de spécifier explicitement l’encodage de bash lors de l’utilisation de iconv . De plus, nous devrions append une marque d’ordre des octets ( même si la norme unicode ne le recommande pas ) car il peut y avoir des confusions légitimes entre UTF-8 et ASCII sans marque d’ordre des octets . Malheureusement, iconv ne fait pas précéder une marque d’ordre d’octet lorsque vous spécifiez explicitement une endianness ( UTF-16BE ou UTF-16LE ). Nous devons donc utiliser UTF-16 , qui utilise l’endianness spécifique à la plateforme, puis utiliser le file --mime-encoding pour découvrir la véritable iconv utilisée.

(Je mets en majuscule tous mes encodages car lorsque vous iconv tous les encodages supportés par iconv -l avec iconv -l ils sont tous en majuscules.)

 # 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 

Ma solution de contournement utilisait Perl:

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

Ma solution de rechange utilisait gnu sed . A bien fonctionné pour mes fins.