Puis-je utiliser `sed` pour traduire des caractères comme avec` tr`?

14

Je voudrais remplacer un ensemble de caractères par des caractères correspondants d'un autre ensemble, quelque chose comme ceci:

original set: ots
"target" set: u.x

foobartest → fuubar.ex.

Les traductions / translittérations comme celle-ci sont la spécialité de la trcommande:

$ echo 'foobartest' | tr 'ots' 'u.x'
fuubar.ex.

Malheureusement, trne prend pas en charge la modification des fichiers sur place comme le sedfait.
Je voudrais l'utiliser sedpour ne pas avoir à réinventer la roue de jongler avec les fichiers temporaires.

n.st
la source
Répondant à cette question car je n'ai trouvé aucun résultat pour "sed translate characters". Le mot-clé magique a fini par être "translittéré", mais j'ai pensé qu'il valait la peine de rendre cette fonctionnalité aussi facilement que possible.
n.st
Quelque chose à garder à l'esprit lorsque vous essayez d'implémenter des solutions de contournement pour cela: tr(correctement) ignore la récursivité dans les ensembles de remplacement: echo 'abc' | tr ab bxbxc. Une solution primitive pourrait supprimer cela xxccar elle réapplique la traduction aux caractères qui ont déjà été traduits.
2017
EN RELATION : TR analogique pour les caractères Unicode? (GNU sedcontrairement à GNU trpeut transliter des caractères multi-octets)
Stéphane Chazelas
Si vous voulez une autre possibilité: perl peut traduire, et -i, et (sauf ancien) multi-octets. Pas POSIX, mais assez courant.
dave_thompson_085

Réponses:

24

seda la ycommande qui fonctionne comme tr:

$ echo 'foobartest' | sed 'y/ots/u.x/'
fuubar.ex.

La ycommande fait partie de la spécification POSIXsed , elle devrait donc fonctionner sur à peu près n'importe quelle plate-forme.

Et comme c'est le cas sed, vous pouvez le faire remplacer un fichier par sa version modifiée, vous épargnant les affaires de fichiers temporaires gênants (à condition que votre implémentation de sedprend en charge l' -ioption, qui n'est pas spécifiée par POSIX):

$ sed -i 'y/ots/u.x/' some-file.txt
n.st
la source
@ StéphaneChazelas Merci de l'avoir signalé; Je n'étais pas au courant du fonctionnement intérieur jusqu'à présent. J'ai édité ma réponse pour le mentionner.
n.st
Merci, c'est extrêmement utile! Je m'attendais à ce qu'il fonctionne dans VIM (8.0.1092 sur CentOS 7.3) mais ce n'est pas le cas. N'est-ce pas quelque chose que sed fait, VIM?
dotancohen
1
@dotancohen Ce n'est pas parce que la fonction de substitution de Vim est modélisée après sedque les autres fonctions le sont aussi. ;) La liste de diffusion Vim a un fil sur la recherche d'un y/abc/def/équivalent; la meilleure option semble être :%call setline(".", tr(getline("."),"abc","def")).
2017
8

Si comme dans votre cas, vous translittérez des caractères sans changer leur taille (de toute façon, certaines implémentations comme GNU trne prennent en charge que les caractères à un octet), vous pouvez faire:

tr 'ots' 'u.x' < file 1<> file

Autrement dit, trécrasez le fichier sur lui-même.

C'est mieux que sed -isur plusieurs comptes:

  • il n'a pas besoin d'espace disque supplémentaire (à l'exception de certains fichiers clairsemés, des cas spéciaux de copie sur écriture)
  • il préserve les numéros d'inode, la propriété, les permissions, les ACL ...
  • ça marche bien avec les liens symboliques, ça ne casse pas les liens durs
  • il ne laisse pas traîner les fichiers temporaires lorsqu'ils sont tués.

Un inconvénient est que s'il est interrompu, le fichier finira par être à moitié traduit (dans ce cas, cependant, vous pouvez le réexécuter pour le terminer). Certaines sedimplémentations géreraient cela correctement en s'assurant que le fichier d'origine reste inchangé à moins que la commande réussisse.

Stéphane Chazelas
la source
3
Soyez prudent en réexécutant la traduction si vous avez une récursivité dans les ensembles de traduction, par exemple echo 'abc' | tr ab bx.
n.st
1
@ n.st, oui, c'est pourquoi j'ai dit dans ce cas , bien que je convienne que cela vaut la peine de le préciser.
Stéphane Chazelas
En fin de compte, je devais travailler avec des fichiers temporaires après tout: gist.github.com/n-st/048facd0c12f105ac122030fb58b962f - Les caractères multi-octets rendaient impossible l'utilisation de GNU tret dans notre environnement PXE lourd de liens symboliques, sed -iétait une attente de vissage pour arriver…: /
n.st
@ n.st, iconv -t cp437semble plus approprié pour cela.
Stéphane Chazelas
iconvse casse lorsque le fichier d'entrée contient déjà des octets encodés en cp437, ou un mélange de plusieurs encodages. Donc, bien qu'il soit préférable dans le cas général, il est plus robuste de faire des remplacements manuels sur ce cas.
2017 à 7h11
4

Comme autre alternative, si votre problème principal est le manque de support pour changer les fichiers sur place, vous pourriez être intéressé par l' spongeoutil du paquet moreutils :

tr 'ots' 'u.x' < file | sponge file

va écrire dans file, mais ne sera ouvert fileen écriture qu'une fois la saisie terminée. Depuis la page de manuel :

spongelit l'entrée standard et l'écrit dans le fichier spécifié. Contrairement à une redirection de shell, l'éponge absorbe toutes ses entrées avant d'ouvrir le fichier de sortie. Cela permet de construire des pipelines qui lisent et écrivent dans le même fichier.

Sauf si vous avez des fichiers vraiment volumineux qui ne peuvent pas être conservés en mémoire, cela spongepourrait fonctionner pour vous.

mindriot
la source
2
Un problème spongeest qu'il écrase toujours en filecas d' tréchec (par exemple si vous aviez accès en écriture mais pas en lecture file)
Stéphane Chazelas
Oh, en effet c'est le cas; Je ne m'attendais pas à ça. Merci.
mindriot
Voir l' cat file >; fileopérateur de ksh93 qui écrit la sortie dans un fichier temporaire qui est renommé à la destination uniquement si la commande réussit (mais comme sed -i, cela crée un nouveau fichier au lieu d'écraser l'original).
Stéphane Chazelas