Remplacez les caractères non imprimables en perl et sed

11

J'ai besoin de remplacer certains caractères non imprimables par des espaces dans le fichier.

Plus précisément, tous les caractères de 0x00jusqu'à 0x1F, sauf 0x09(TAB), 0x0A(nouvelle ligne), 0x0D(CR)

Jusqu'à présent, j'avais juste besoin de remplacer le 0x00personnage. Étant donné que mon système d'exploitation précédent était AIX (sans commandes GNU), je ne peux pas l'utiliser sed(enfin, je le peux, mais il y avait quelques limitations). J'ai donc trouvé la prochaine commande utilisant perl, qui a fonctionné comme prévu:

perl -p -e 's/\x0/ /g' $FILE_IN > $FILE_OUT 

Maintenant, je travaille sur Linux, donc je m'attendais à pouvoir utiliser la sedcommande.

Mes questions:

  • Cette commande est-elle appropriée pour remplacer ces caractères? J'ai essayé, et cela semble fonctionner, mais je veux m'assurer:

    perl -p -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT  
  • Je pensais perl -pque ça marche sed. Alors, pourquoi la commande précédente fonctionne-t-elle (au moins, elle n'échoue pas) et la suivante non?

    sed -e 's/[\x00-\x08\x0B\x0C\x0E-\x1F]/ /g' $FILE_IN > $FILE_OUT   

    Ça me dit:

    sed: -e expression # 1, caractère 34: caractère de classement non valide

Albert
la source
perl -pimprime le produit final stdinaprès avoir effectué les opérations souhaitées, dans ce cas, il s'agit simplement de remplacement. sedLe regex de peut être différent de perl.
sdkks

Réponses:

11

C'est un travail typique pour tr:

LC_ALL=C tr '\0-\10\13\14\16-\37' '[ *]' < in > out

Dans votre cas, cela ne fonctionne pas, sedcar vous vous trouvez dans une région où ces plages n'ont aucun sens. Si vous voulez travailler avec des valeurs d'octets par opposition aux caractères et où l'ordre est basé sur la valeur numérique de ces octets, votre meilleur pari est d' utiliser les paramètres régionaux C . Votre code aurait fonctionné avec LC_ALL=CGNU sed, mais utiliser sed(sans parler perl) est un peu exagéré ici (et ceux \xXX-ci ne sont pas portables sur toutes les sedimplémentations alors que cette trapproche est POSIX).

Vous pouvez également faire confiance à l'idée de votre région de ce que sont les caractères imprimables avec:

tr -c '[:print:]\t\r\n' '[ *]'

Mais avec GNU tr(comme on le trouve généralement sur les systèmes Linux), cela ne fonctionne que dans les paramètres régionaux où les caractères sont à un octet (donc généralement pas UTF-8).

Dans les paramètres régionaux C, cela exclurait également DEL (0x7f) et toutes les valeurs d'octets ci-dessus (pas en ASCII).

Dans les locales UTF-8, vous pouvez utiliser GNU sedqui n'a pas le problème que GNU tra:

sed 's/[^[:print:]\r\t]/ /g' < in > out

(notez que ceux \r, \tne sont pas standard, et GNU sedne les reconnaîtra pas si POSIXLY_CORRECTest dans l'environnement (les traitera comme backslash, r et t faire partie de l'ensemble comme POSIX exige)).

Cependant, il ne convertirait pas les octets qui ne forment pas de caractères valides.

Stéphane Chazelas
la source
Je comprends ce que fait la trcommande. Je comprends (plus ou moins) ce qui LC_ALL = Cest, mais pas tous ensemble. tr -dSupprime néanmoins ces caractères, mais je veux les remplacer par des espaces. Désolé, le titre était incorrect. Je viens de réaliser, quand @don_crissti a été modifié.
Albert
@Albert, désolé. Voir la modification et le lien que j'ai ajouté.
Stéphane Chazelas
Je ne suis pas sûr de l'encodage. Ce fichier provient d'un environnement HOST, qui utilise le codage EBCDIC, et il est transféré vers Linux en utilisant XCOM. Par exemple, les caractères non ASCII comme Ésont codifiés (en utilisant od -xa) comme 0xC9, donc je suppose que ce le serait ISO-8859-1.
Albert
@Albert, probablement. Vous pouvez utiliser locale -apour voir s'il existe des paramètres régionaux avec iso8859-1 comme jeu de caractères sur votre système et utiliser LC_CTYPE=<that-locale> tr ...[:print:]...pour convertir les éléments non imprimables dans ce paramètre régional. Ou vous pouvez utiliser iconv pour convertir ces fichiers en jeu de caractères de votre locale.
Stéphane Chazelas
Je pense que ce n'est pas nécessaire, car le jeu de caractères de mes paramètres régionaux est défini sur LC_ALL=en_US.iso88591. Ainsi, votre commande ( tr -c '[:print:]\t\r\n' '[ *]') fonctionne parfaitement sans modifier les paramètres régionaux ou convertir le fichier. Merci beaucoup.
Albert
0

J'essayais d'envoyer une notification via libnotify, avec du contenu pouvant contenir des caractères non imprimables. Les solutions existantes ne fonctionnaient pas tout à fait pour moi (utiliser une liste blanche de caractères utilisant des trœuvres, mais supprime tous les caractères multi-octets).

Voici ce qui a fonctionné, en passant le test 💩:

message=$(iconv --from-code=UTF-8 -c <<< "$message")
Nous sommes tous Monica
la source