Expression régulière pour replace une instance de deux strings consécutives qui pourraient être séparées par des espaces

Je veux écrire une ligne perl qui remplace chaque instance de deux strings consécutives spécifiques qui peuvent ou non être séparées par des espaces.

Par exemple, dites que mes deux strings sont john paul et george et je veux replace les instances consécutives de ces strings (dans cet ordre) par pete . Exécuter le one-liner sur

 $ cat ~/foo john paulgeorge john paul george john paul george george john paul 

devrait aboutir à

 $ cat ~/foo pete pete pete george john paul 

La seule chose à laquelle j'ai pensé est

 $ perl -p -i -e 's/john paul\s*george/pete/g' ~/foo 

mais cela se traduit par

 $ cat ~/foo pete pete john paul george george john paul 

Y a-t-il un moyen de modifier mon monoplan?

La seule chose que vous devez append à votre one-liner est l'option de slurp le file comme une seule string:

 perl -0777 -p -i -e 's/john paul\s*george/pete/g' ~/foo # ^^^^^ 

Voir http://perldoc.perl.org/perlrun.html#Command-Switches

Les options -n et -p perl mettent des variantes de while (<>) { ... } autour de votre programme, ce qui leur permet de traiter les inputs ligne par ligne. Si vous voulez replace sur plusieurs lignes, vous devez lire le tout dans une string, que vous devez faire vous-même.

 perl -e 'local $/;$_=<>;s/john paul\s*george/pete/g;print' 

Cela dé fi nit $/ , le séparateur d'loggings , de sorte que <> slurping ne fera plus de division de ligne, lit l'input entière dans $_ à la fois, puis effectue le rlocation sur cette longue string. Vous devez également faire votre propre printing.

Il n'y a plus beaucoup de magie ici – c'est juste écrire un programme Perl complet d'une manière un peu inconfortable. -i fonctionnera toujours pour le rlocation sur place, cependant.

Si vous avez un gros file cela va être assez inefficace (ou épuiser votre memory), mais cela semble plus ou less inévitable sans build un meilleur parsingur. Vous pouvez également voir perldoc -q 'entire file' pour d'autres alternatives et beaucoup de vous dire que vous ne le voulez pas vraiment.

Avec sed vous pouvez le faire sans supprimer le file entier:

 sed -e ':top' -e 's/john paul[[:space:]]*george/pete/g;$b' -e '/john paul[[:space:]]*$/!b' -e 'N;btop' input 

Ceci est beaucoup plus léger sur l'utilisation de la memory; il ne traîne que plusieurs lignes lorsqu'il existe une possibilité de correspondance multiligne à partir de la ligne actuelle. Et puis il ne fait que slurps jusqu'à ce que le match soit trouvé, ou jusqu'à ce qu'il n'y ait plus de possibilité de match.

En prime, il est compatible POSIX. (Perl ne fait pas partie de POSIX.) Merci à mikeserv pour le signaler dans les commentaires.

Explication:

:top définit une label nommée top .

s/john paul[[:space:]]*george/pete/g fait la substitution que vous voulez pour tout ce qui se trouve dans l'espace du motif. (La valeur par défaut est ligne par ligne.)

$b saute à la fin et imprime si la ligne actuelle est la dernière ligne du file.

/john paul[[:space:]]*$/!b :

Le model /john paul[[:space:]]*$/ correspondra à john paul à la fin de l'espace motif suivi de toute quantité d'espace (mais rien d'autre que des espaces), alors ! inverse le motif. Donc, l'effet est ici d'exécuter la command b (passez à la fin du script, imprimant l'espace du patron, lisant la ligne suivante du file et partant du haut du script) seulement s'il n'y a pas de possibilité multiligne en commençant par l'espace model actuel.

N ajoute la ligne suivante du file à l'espace du motif (après avoir ajouté une nouvelle ligne).

btop twigs à l'label :top sans effacer l'espace de motif.

Un autre sed :

 s=[:space:] sed -e:t -e$\!"N;s/john paul[$s]*george/pete/g;/\n/"\!tt -e"P;D" <in >out 

Cela permettra de gérer toutes les occurrences de votre string en une seule substitution, et seulement tampon aussi peu que nécessaire. Il fonctionne via une window glissante en input, et ne ramène que les twigs pour extraire les nouvelles lignes si la substitution précédente a remplacé avec succès votre string et, par conséquent, a supprimé un caractère de nouvelle ligne dans le process.

Le bizarre ! la citation n'est nécessaire que dans un shell par défaut (read: insane) interactive (ba|z|t?c)sh , mais n'est généralement pas un problème dans un shell scripté (sauf si vous avez une variante csh ) .

Vous aurez besoin de slurp le file avec l'option -0777. Mais vous devriez également append les modificateurs m à la fin afin de s'assurer que \ s correspondra aussi à \ n.

Quand Perl voit -0, il mettra à jour le séparateur d'inputs ($ /) avec ce qui suit. Par exemple si j'aurais mis -00, Perl aurait mis le mode paragraphe $ / in. Alors

 perl -0777 -pe 's/^john paul\s*george/pete/gm' george.txt 

est équivalent à :

 perl -pe 'BEGIN { undef $/ ; } s/^john paul\s*george/pete/gm' george.txt