2013-03-05 4 views
0

J'utilise la version 4.2.1 de GNU sed et j'essaye d'écrire une regex SED non gourmande pour extraire une chaîne délimitée par deux autres chaînes. C'est facile lorsque les chaînes de délimitation sont un seul caractère:Comment écrire une regex SED pour extraire une chaîne délimitée par une autre chaîne?

s:{\([^}]*\)}:\1:g 

Dans cet exemple, la chaîne est délimitée par « { » à gauche et « } » à droite.

Si les chaînes délimitant sont multiples caractères, dire « {{{ » et '}}} Je peux régler l'expression ci-dessus comme ceci:

s:{{{\([^}}}]*\)}}}:\1:g 

donc l'expression centrale correspond à tout ce ne contenant pas le' }}} 'chaîne de fermeture. Mais cela ne fonctionne que si la chaîne de correspondance ne contient pas de '}'. Quelque chose comme:

{{{cannot match {this broken} example}}} 

ne fonctionnera pas, mais

{{{can match this example}}} 

fonctionne. Bien sûr

s:{{{\(.*\)}}}:\1:g 

fonctionne toujours, mais est gourmand donc ne convient pas où plusieurs motifs se produisent sur la même ligne.

Je comprends [^a] à dire quoi que ce soit, sauf a et [^ab] signifier quoi que ce soit, sauf a ou b donc, bien qu'il apparaît au travail, je ne pense pas [^}}}] est la bonne façon d'exclure cette séquence de 3 caractères consécutifs.

Alors, comment écrire une regex pour SED qui correspond à une chaîne délimitée par deux autres chaînes?

Répondre

1

Vous avez raison: [^}}}] ne fonctionne pas. Une classe de caractères niée correspond à tout ce qui n'est pas l'un des caractères qui s'y trouvent. La répétition de caractères ne change pas la logique. Donc, ce que vous avez écrit est le même que [^}]. (Il est facile de voir pourquoi cela fonctionne quand il n'y a pas d'accolades dans l'expression).

En Perl et des expressions régulières compatibles, vous pouvez utiliser ? pour faire un * ou + non gourmand:

s:{{{(.*?)}}}:$1:g 

Cela correspondra toujours la première }}} après l'ouverture {{{.

Cependant, this is not possible in Sed. En fait, je ne pense pas qu'il y ait moyen à Sed de faire ce match. La seule autre façon de le faire est d'utiliser des fonctionnalités avancées telles que le look-ahead, que Sed n'a pas non plus.

Vous pouvez facilement utiliser Perl de façon comme sed avec les -pe options, qui la font prendre une seule ligne de code à partir de la ligne de commande (-e) et la boucle automatiquement sur chaque ligne et imprimer le résultat (-p) .

perl -pe 's:{{{(.*?)}}}:$1:g' 

Le est également utile, mais assurez-vous que votre regex est correcte première option -i pour le montage en place des fichiers!

Pour plus d'informations, voir perlrun.

+0

merci pour votre réponse - c'est ce que je soupçonnais car je savais que sed ne pouvait pas regarder en avant. J'ai trouvé que je n'avais pas besoin d'échapper au groupe de capture dans votre exemple: 's: {{{(. *?)}}}: $ 1 <: g'' (en fait, quand je l'ai fait, il ne l'a pas fait t travail). – starfry

+0

@starfry, oups, vous avez raison sur le groupe de capture. C'était une faute de frappe. –

0

Avec sed vous pourriez faire quelque chose comme:

sed -e :a -e 's/\(.*\){{{\(.*\)}}}/\1\2/ ; ta' 

Avec:

{{{can match this example}}} {{{can match this 2nd example}}} 

Cela donne:

can match this example can match this 2nd example 

Il ne correspond pas à paresseux, mais en remplaçant de droite à à gauche, nous pouvons utiliser la gourmandise de sed.

Questions connexes