J'essaie de remplacer une chaîne dans un Makefile sur Mac OS X pour une compilation croisée vers 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:
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
Je passe beaucoup de temps à déboguer le problème. Quelqu'un sait-il comment sed
imprimer la position de la séquence d'octets illégale? Ou est-ce que quelqu'un sait quelle est la séquence d'octets illégale?
LC_CTYPE=C && LANG=C && sed command
LANG
chose. Soupir ....sed
(tel qu'utilisé également sur OS X) requiert-i ''
(argument d'option de chaîne vide distinct) pour la mise à jour sur place sans fichier de sauvegarde; avec GNUsed
, ne-i
fonctionne que par lui-même - voir stackoverflow.com/a/40777793/45375Réponses:
Un exemple de commande qui présente le symptôme:
sed 's/./@/' <<<$'\xfc'
échoue, car l'octet0xfc
n'est pas un caractère UTF-8 valide.Notez que, en revanche, GNU
sed
(Linux, mais également installable sur macOS) passe simplement 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 la prise en charge de votre véritable locale (si vous êtes sur un système américain et que vous n'avez jamais besoin de gérer des caractères étrangers, cela peut être bien.)
Cependant, le même effet peut être obtenu ad hoc pour une seule commande uniquement :
LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Remarque: Ce qui compte, c'est un réglage efficace
LC_CTYPE
deC
, doncLC_CTYPE=C sed ...
cela fonctionnerait normalement aussi, mais s'ilLC_ALL
se trouve être défini (sur autre chose queC
), il remplacera lesLC_*
variables de catégorie individuelles telles queLC_CTYPE
. Ainsi, l'approche la plus robuste est de définirLC_ALL
.Cependant, le réglage (effectivement)
LC_CTYPE
surC
traite les chaînes comme si chaque octet était son propre caractère ( aucune interprétation basée sur des règles de codage n'est effectuée), sans égard pour le codage UTF-8 multi-octets à la demande qu'OS X utilise par défaut , où les caractères étrangers ont des encodages multi - octets .En un mot: le réglage
LC_CTYPE
surC
fait que le shell et les utilitaires ne reconnaissent que les lettres anglaises de base comme des lettres (celles de la plage ASCII 7 bits), de sorte que les 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 convenir si vous n'avez pas besoin de faire correspondre des caractères codés sur plusieurs octets tels que
é
, et que vous souhaitez simplement passer ces caractères .Si cela est insuffisant et / ou si vous souhaitez 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 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 encodés d'une manière qui n'est pas valide en UTF-8 (comme @Klas Lindbäck l'a déclaré dans un commentaire) - c'est ce que le
sed
message d'erreur essaie de direinvalid byte sequence
.Très probablement, votre fichier d'entrée utilise un codage 8 bits à un octet 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 Unicode0xE0
(224) - le même que dansISO-8859-1
. Cependant, en raison de la nature du codage UTF-8 , ce point de code unique est représenté par 2 octets -0xC3 0xA0
, alors que tenter de passer l' octet unique0xE0
est invalide sous UTF-8.Voici une démonstration du problème en utilisant la chaîne
voilà
codée commeISO-8859-1
, avec leà
représenté comme un octet (via une chaîne bash entre guillemets ANSI-C ($'...'
) qui utilise\x{e0}
pour créer l'octet):Notez que la
sed
commande est effectivement 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 ignorer simplement le problème , l'
LCTYPE=C
approche 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 , 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 vous montrera tous les octets qui ont le bit haut défini (octets qui dépassent la plage ASCII 7 bits) sous forme hexadécimale. (Notez, cependant, que cela inclut également les séquences multioctets UTF-8 correctement codées - une approche plus sophistiquée serait nécessaire pour identifier spécifiquement les octets non valides en UTF-8.)
Effectuer des conversions d'encodage à la demande :
L'utilitaire standard
iconv
peut être utilisé pour convertir des encodages en (-t
) et / ou à partir de (-f
);iconv -l
répertorie tous ceux pris en charge.Exemples:
Convertissez FROM
ISO-8859-1
en encodage en vigueur dans le shell (basé surLC_CTYPE
, qui estUTF-8
basé par défaut), en vous basant sur 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, dirigez simplement le résultat vers une autreiconv
commande:sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1
la source
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
imprimesed: RE error: illegal byte sequence
pour moi sur Sierra.echo $LC_ALL
sortiesen_US.UTF-8
FWIW.LC_ALL
remplace toutes les autresLC_*
variables, y comprisLC_CTYPE
, comme expliqué dans la réponse.Ajoutez les lignes suivantes à vos fichiers
~/.bash_profile
ou~/.zshrc
.export LC_CTYPE=C export LANG=C
la source
LC_CTYPE
surC
fait que chaque octet des chaînes est son propre caractère sans appliquer de règles de codage. Étant donné qu'une violation des règles de codage (UTF-8) a provoqué le problème d'origine, le problème disparaît. Cependant, le prix à payer est que le shell et les utilitaires ne reconnaissent alors que les lettres anglaises de base (celles de la gamme ASCII 7 bits) comme des lettres. Voir ma réponse pour plus.LC_CTYPE=C sed …
, c'est à dire uniquement sur la commande sed.Ma solution de contournement utilisait Perl:
find . -type f -print0 | xargs -0 perl -pi -e 's/was/now/g'
la source
La réponse de mklement0 est excellente, mais j'ai quelques petits ajustements.
Il semble judicieux de spécifier explicitement
bash
le codage de lors de l 'utilisationiconv
. De plus, nous devrions ajouter une marque d'ordre d'octet ( même si le standard Unicode ne le recommande pas ) car il peut y avoir des confusions légitimes entre UTF-8 et ASCII sans marque d'ordre d'octet . Malheureusement,iconv
ne ajoute pas de marque d'ordre d'octet lorsque vous spécifiez explicitement une endianness (UTF-16BE
ouUTF-16LE
), nous devons donc utiliserUTF-16
, qui utilise l'endianness spécifique à la plate-forme, puis l'utiliserfile --mime-encoding
pour découvrir la vraie endiannessiconv
utilisée.(Je mets tous mes encodages en majuscules car lorsque vous listez tous
iconv
les encodages pris en charge par,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
la source
file -b --mime-encoding
pour découvrir et signaler l'encodage d'un fichier. Cependant, certains aspects méritent d'être abordés, ce que je ferai dans des commentaires séparés.LC_CTYPE
valeur par défaut est généralement<lang_region>.UTF-8
, donc tout fichier sans BOM (marque d'ordre des octets) est donc interprété comme un fichier UTF-8. Ce n'est que dans le monde Windows que la pseudo-nomenclature0xef 0xbb 0xff
est utilisée; par définition, UTF-8 n'a pas besoin d' une nomenclature et n'est pas recommandé (comme vous le dites); en dehors du monde Windows, cette pseudo-nomenclature provoque des pannes .Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE)
: c'est par conception: si vous spécifiez explicitement l'endianness , il n'est pas nécessaire de la refléter également via une nomenclature, donc aucune n'est ajoutée.LC_*
/LANG
variables:bash
,ksh
, etzsh
(peut - être d' autres, mais pasdash
) faire respecter le codage de caractères; vérifier dans des shells de type POSIX avec une locale basée surv='ä'; echo "${#v}"
UTF-8 avec : un shell compatible UTF-8 devrait signaler1
; c'est-à-dire qu'il doit reconnaître la séquence multi-octetsä
(0xc3 0xa4
), comme un seul caractère. Peut-être plus important encore, cependant: les utilitaires standards (sed
,awk
,cut
, ...) doivent également être locale / encodage-courant, et alors que la plupart d'entre eux sur des plates - formes modernes de type Unix sont, il y a des exceptions, commeawk
sur Mac OS X, etcut
sur Linux.file
reconnaître la pseudo-nomenclature UTF-8, mais le problème est que la plupart des utilitaires Unix qui traitent des fichiers ne le font pas et se cassent généralement ou se comportent du moins mal lorsqu'ils sont confrontés à un fichier . Sans nomenclature,file
identifie correctement un fichier d'octets de 7 bits comme ASCII et un fichier qui a des caractères multi-octets UTF-8 valides comme UTF-8. La beauté de l'UTF-8 est qu'il s'agit d'un sur - ensemble d'ASCII: tout fichier ASCII valide est par définition un fichier UTF-8 valide (mais pas l'inverse); il est parfaitement prudent de traiter un fichier ASCII comme UTF-8 (ce qui est techniquement, il se trouve qu'il ne contient aucun caractère multi-octets.)Vous devez simplement diriger une commande iconv avant la commande sed . Ex avec entrée file.txt:
L' option -f est le jeu de codes 'from' et l'option -t est la conversion du jeu de codes 'vers'.
Faites attention à la casse, les pages Web affichent généralement des minuscules comme ça <charset = iso-8859-1 "/> et iconv utilise des majuscules. Vous avez une liste des jeux de codes pris en charge par iconv dans votre système avec la commande iconv -l
UTF8-MAC est un jeu de codes OS Mac moderne pour la conversion.
la source
J'ai fait une partie du chemin pour répondre à ce qui précède simplement en utilisant tr .
J'ai un fichier .csv qui est un relevé de carte de crédit et j'essaye de l'importer dans Gnucash. Je suis basé en Suisse et je dois donc composer avec des mots comme Zürich. Suspectant que Gnucash n'aime pas "" dans les champs numériques, je décide de tout simplement remplacer
avec
Voici:
$ head -3 Auswertungen.csv | tail -1 | sed -e 's/; ;/;;/g' sed: RE error: illegal byte sequence
J'ai utilisé od pour faire la lumière: notez le 374 à mi-chemin de cette sortie od -c
Ensuite, j'ai pensé que je pourrais essayer de persuader tr de remplacer 374 par quel que soit le code d'octet correct. Alors j'ai d'abord essayé quelque chose de simple, qui n'a pas fonctionné, mais qui a eu pour effet secondaire de me montrer où se trouvait l'octet gênant:
$ head -3 Auswertungen.csv | tail -1 | tr . . ; echo tr: Illegal byte sequence 1687 9619 7122;5468 87XX XXXX 2660;MY NAME ISX;14.02.2019;9552 - Mitarbeiterrest Z
Vous pouvez voir tr bails au caractère 374.
L'utilisation de perl semble éviter ce problème
$ head -3 Auswertungen.csv | tail -1 | perl -pne 's/; ;/;;/g' 1687 9619 7122;5468 87XX XXXX 2660;ADAM NEALIS;14.02.2019;9552 - Mitarbeiterrest Z?rich CHE;Restaurants, Bars;6.20;CHF;;CHF;6.20;;15.02.2019
la source
Ma solution de contournement utilisait gnu
sed
. A bien fonctionné pour mes besoins.la source
sed
est une option si vous voulez ignorer les octets invalides dans le flux d'entrée (pas besoin de laLC_ALL=C sed ...
solution de contournement), car GNU passesed
simplement les octets invalides au lieu de signaler une erreur, mais notez que si vous voulez bien reconnaître et traiter tous caractères dans la chaîne d'entrée, il n'y a aucun moyen de modifier d'abord le codage de l'entrée (généralement, aveciconv
).