Comment remplacer récursivement des personnages par sed?

13

Est-il possible de remplacer les occurrences d'une séquence de caractères de manière récursive sans recommencer sur la même séquence?

En effectuant un sedcomme dans les scénarios suivants, je peux obtenir la sortie mentionnée.

$ echo XX | sed -e 's/XX/XoX/g'
XoX  
$ echo XXX | sed -e 's/XX/XoX/g'
XoXX  
$ echo XXXX | sed -e 's/XX/XoX/g'
XoXXoX  

Cependant, je m'attends à ce que la sortie suive le comportement suivant.

Contribution:

XX
XXX
XXXX

Production attendue:

XoX
XoXoX
XoXoXoX

Est-il possible d'atteindre le comportement attendu avec sed seul?

Ishan Madhusanka
la source

Réponses:

24

Tu peux faire:

> echo XXXX | sed -e ':loop' -e 's/XX/XoX/g' -e 't loop'
XoXoXoX

Avec:

  • -e ':loop' : Créer une étiquette "boucle"
  • -e 't loop' : Aller à l'étiquette "boucle" si la substitution précédente a réussi
Gohu
la source
10

Dans ce cas particulier, une analyse prospective ou rétrospective serait utile. Je pense que GNU sedne les prend pas en charge. Avec perl:

perl -ne 's/X(?=X)/Xo/g; print;'

Vous pouvez également utiliser lookbehind et lookahead comme:

s/(?<=X)(?=X)/o/g

Où:

(?<=X)est un lookbehind positif, une assertion de longueur nulle qui s'assure que nous avons un X avant la position actuelle
(?=X)est une anticipation positive, une assertion de longueur nulle qui s'assure que nous avons un X après la position actuelle

Utilisation en Perl one-liner:

perl -pe 's/(?<=X)(?=X)/o/g' inputfile

Où:

-p oblige Perl à assumer une boucle autour du programme avec une impression implicite de la ligne courante

Toto
la source
5

La réponse en boucle est la façon générale de faire ce que vous demandez.

Cependant, dans le cas de vos données, en supposant que vous utilisez GNU, vous pouvez simplement faire:

sed 's/\B/o/g'

Les options \bet \Bsont des extensions regex :

  • \b correspond aux limites des mots, c'est-à-dire la transition d'un caractère "mot" à un caractère "non-mot", ou vice-versa
  • \Bcorrespond à l'opposé de \b. c'est-à-dire les lacunes "à l'intérieur" des mots. Cela nous permet d'insérer des caractères à l'intérieur d'un mot mais pas à l'extérieur, comme requis.

Essayez-le en ligne .

Cela suppose que les caractères saisis sont en fait tous des caractères "word".


Alternativement, si vous n'avez pas GNU sed, ou si les caractères d'entrée ne sont pas tous des caractères "word", vous pouvez toujours atteindre votre objectif sans boucler:

sed 's/./&o/g;s/o$//'

Cela place simplement un oaprès chaque caractère, puis supprime la finale ode la chaîne.

Essayez-le en ligne .

Traumatisme numérique
la source
1
Cela suppose que les chaînes d'entrée se composent d'un certain nombre de Xet rien d'autre. Les deux solutions échouent si d'autres personnages sont présents ...
AnoE
@AnoE Dans le deuxième exemple, cela est corrigé avec un simple remplacement de Xpar .. Veuillez voir modifier.
Digital Trauma
Pas équivalent au cas donné par le PO. Il a donné les RE exactes dont il avait besoin (changer les occurrences de XX dans une chaîne). Vos versions ne donnent le même résultat que le sien pour les mêmes chaînes d'entrée exactes qu'il a données; pas pour les chaînes d'entrée génériques.
AnoE
4

J'ai vérifié s'il y avait une sorte de drapeau pour que cela se produise.
Même si ce comportement existait, il consommera beaucoup de ressources.

Cependant, dans ce cas d'utilisation particulier, il est possible d'avoir l'expression deux fois seulement et d'obtenir la fonctionnalité requise. c'est-à-dire avec 2 sedexpressions répétitives .

echo XX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'     # outputs XoX
echo XXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'    # outputs XoXoX
echo XXXX | sed -e 's/XX/XoX/g' -e 's/XX/XoX/g'   # outputs XoXoXoX
Ishan Madhusanka
la source