J'essaie de remplacer une chaîne dans un Makefile sur Mac OS X pour une compilation croisée sur iOS. La chaîne contient des guillemets doubles. La commande est:
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, aux virgules, aux tirets et aux deux points sans joie. Par exemple:
sed -i "" 's|\"iphoneos-cross\"\,\"llvm-gcc\:\-O3|\"iphoneos-cross\"\,\"clang\:\-Os|g' Configure
Je passe énormément de temps à résoudre le problème. Est-ce que quelqu'un sait comment obtenir sed
pour imprimer la position de la séquence d'octets illégale? Ou est-ce que quelqu'un sait ce qu'est la séquence d'octets illégale?
Un exemple de commande présentant le symptôme suivant: sed 's/./@/' <<<$'\xfc'
échoue, car l'octet 0xfc
n'est pas un caractère UTF-8 valide.
Notez que, au contraire, GNUsed
(Linux, mais peut également être installé sur macOS) ne fait que passer l'octet invalide, sans générer de rapport. Erreur.
L'utilisation de réponse autrefois acceptée est une option possible si vous ne craignez pas de perdre le support de vos paramètres régionaux véritables (si vous êtes sur un système américain et que vous n'avez jamais besoin de vous en occuper. caractères étrangers, ça peut aller.)
Cependant, le même effet peut être eu ad-hoc pour une commande unique uniquement:
LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Remarque: ce qui compte est un réglage efficace LC_CTYPE
de C
, de sorte que LC_CTYPE=C sed ...
serait normalement fonctionne également, mais si LC_ALL
est défini (sur autre chose que C
), il remplacera les variables individuelles LC_*
- telles que LC_CTYPE
. Ainsi, l’approche la plus robuste consiste à définir LC_ALL
.
Cependant, régler (effectivement) LC_CTYPE
sur C
traite les chaînes comme si chaque octet était son propre caractère ( no basé sur l'interprétation sur les règles de codage est effectuée), avec no égard pour - multibyte-on-demand - codage UTF-8 utilisé par OS X par défaut, où foreign caractères ont codages multi-octets.
En bref: paramètre LC_CTYPE
à C
permet au shell et aux utilitaires de ne reconnaître que les lettres anglaises de base en tant que lettres (celles du 7 bits ASCII gamme), de sorte que caractères étrangers. ne seront pas traités comme des lettres, ce qui entraînera, par exemple, l'échec des conversions majuscules/minuscules.
Encore une fois, cela peut suffire si vous n'avez pas besoin de faire correspondre des caractères codés sur plusieurs octets, tels que é
, et que vous souhaitiez simplement les transmettre. par.
Si cela est insuffisant et/ou si vous voulez comprendre la cause de l'erreur d'origine (y compris déterminer quels octets d'entrée ont causé le problème) et effectuer des conversions de codage à la demande, - lisez la suite ci-dessous.
Le problème est que l'encodage 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 de manière non valide en UTF-8 (comme @Klas Lindbäck l'a indiqué dans un commentaire) - c'est ce que la sed
message d'erreur tente de dire par invalid byte sequence
.
Très probablement, votre fichier d'entrée utilise un codage sur un octet sur 8 bits tel que ISO-8859-1
, fréquemment utilisé pour coder les langues "d'Europe occidentale".
Exemple:
La lettre accentuée à
a un point de code Unicode 0xE0
(224) - comme dans ISO-8859-1
. Cependant, en raison de la nature du codage UTF-8 , ce code unique est représenté par 2 octets - 0xC3 0xA0
, alors que l’essai de transmettre l’octet unique 0xE0
est invalide sous UTF-8.
Voici une démonstration du problème utilisant la chaîne voilà
codée en tant que ISO-8859-1
, avec le à
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 fait un no-op 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 ci-dessus LCTYPE=C
peut être utilisée:
# No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
Si vous voulez déterminer quelles parties de l'entrée sont à l'origine du problème, essayez ce qui 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 affiche tous les octets pour lesquels le bit haut est défini (octets dépassant la plage de 7 bits ASCII) sous forme hexadécimale. (Notez, cependant, que cela inclut également les séquences multi-octets UTF-8 correctement codées - une approche plus sophistiquée serait nécessaire pour identifier spécifiquement les octets invalides dans UTF-8.)
Effectuer des conversions d'encodage à la demande:
L'utilitaire standard iconv
peut être utilisé pour convertir les encodages en (-t
) et/ou à partir de (-f
); iconv -l
répertorie tous ceux pris en charge.
Exemples:
Convertissez FROM ISO-8859-1
en codage en vigueur dans le shell (basé sur LC_CTYPE
, qui est basé sur UTF-8
- par défaut), à partir de 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 diriger 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
Ma solution de contournement utilisait Perl:
find . -type f -print0 | xargs -0 Perl -pi -e 's/was/now/g'
La réponse de mklement c'est bien, mais j'ai quelques petites modifications à apporter.
Cela semble être une bonne idée de spécifier explicitement le codage de bash
lorsque vous utilisez iconv
. De plus, nous devrions ajouter une marque d'ordre des octets ( même si le standard unicode ne le recommande pas ) car il peut y avoir une confusion légitime entre UTF-8 et ASCII sans marque d'ordre des octets . Malheureusement, iconv
ne comporte pas de marque d'ordre d'octet lorsque vous spécifiez explicitement une finalité (UTF-16BE
ou UTF-16LE
), nous devons donc utiliser UTF-16
, qui utilise la plate-forme. -endianness spécifique, puis utilisez file --mime-encoding
pour découvrir le véritable endianness iconv
utilisé.
(Je mets en majuscule tous mes encodages car lorsque vous listez tous les encodages pris en charge par iconv
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
Vous devez simplement diriger une commande iconv avant la commande sed. Ex avec entrée file.txt:
iconv -f ISO-8859-1 -t UTF8-MAC fichier.txt | sed 's/quelquechose/àéèêçùû/g' | .....
- f option est le jeu de codes 'à partir de' et l'option -t est la conversion 'à' jeux de codes.
Attention, les pages Web affichent généralement des minuscules telles que <charset = iso-8859-1 "/> et iconv utilise des majuscules. Vous avez la liste des iconv jeux de codes pris en charge dans votre système avec la commande iconv -l
TF8-MAC est un jeu de codes Mac OS moderne pour la conversion.
Ma solution de contournement utilisait gnu sed
. A bien fonctionné pour mes besoins.