Je veux utiliser sed
pour remplacer quoi que ce soit dans une chaîne entre la première AB
et la première occurrence de AC
(inclus) par XXX
.
Par exemple , j'ai cette chaîne (cette chaîne est pour un test uniquement):
ssABteAstACABnnACss
et je voudrais similaire à ceci: ssXXXABnnACss
.
Je l'ai fait avec perl
:
$ echo 'ssABteAstACABnnACss' | perl -pe 's/AB.*?AC/XXX/'
ssXXXABnnACss
mais je veux l'implémenter avec sed
. Les éléments suivants (à l'aide de l'expression rationnelle compatible Perl) ne fonctionnent pas:
$ echo 'ssABteAstACABnnACss' | sed -re 's/AB.*?AC/XXX/'
ssXXXss
text-processing
sed
regular-expression
بارپابابا
la source
la source
Réponses:
Les expressions régulières sed correspondent à la plus longue correspondance. Sed n'a pas d'équivalent de non gourmand.
Évidemment, ce que nous voulons faire, c'est
AB
,suivi de
AC
,suivi de
AC
Malheureusement,
sed
ne peut pas faire # 2 - du moins pas pour une expression régulière à plusieurs caractères. Bien sûr, pour une expression régulière à un seul caractère comme@
(ou même[123]
), nous pouvons faire[^@]*
ou[^123]*
. Et donc nous pouvons contourner les limites de sed en changeant toutes les occurrences deAC
en@
puis en recherchantAB
,suivi de
@
,suivi de
@
comme ça:
La dernière partie
@
remplace les instances inégalées de back toAC
.Mais, bien sûr, c'est une approche imprudente, car l'entrée peut déjà contenir des
@
caractères, donc, en les faisant correspondre, nous pourrions obtenir des faux positifs. Cependant, étant donné qu'aucune variable shell n'aura jamais de caractère NUL (\x00
), NUL est probablement un bon caractère à utiliser dans la solution de contournement ci-dessus au lieu de@
:L'utilisation de NUL nécessite GNU sed. (Pour vous assurer que les fonctionnalités GNU sont activées, l'utilisateur ne doit pas avoir défini la variable shell POSIXLY_CORRECT.)
Si vous utilisez sed avec l'
-z
indicateur GNU pour gérer les entrées séparées par NUL, telles que la sortie defind ... -print0
, alors NUL ne sera pas dans l'espace de motif et NUL est un bon choix pour la substitution ici.Bien que NUL ne puisse pas être dans une variable bash, il est possible de l'inclure dans une
printf
commande. Si votre chaîne d'entrée peut contenir n'importe quel caractère, y compris NUL, alors voyez la réponse de Stéphane Chazelas qui ajoute une méthode d'échappement intelligente.la source
echo
ouprintf
un \ \ 000 très bien dans bash (ou l'entrée pourrait provenir d'un fichier). Mais en général, une chaîne de texte n'a bien sûr pas de NUL.AC
deAC@
dos?Certaines
sed
implémentations prennent en charge cela.ssed
a un mode PCRE:AT&T ast sed a une conjonction et une négation lors de l'utilisation des expressions rationnelles augmentées :
De manière portable, vous pouvez utiliser cette technique: remplacez la chaîne de fin (ici
AC
) par un seul caractère qui n'apparaît ni dans la chaîne de début ni dans la chaîne de fin (comme:
ici) pour que vous puissiez le faires/AB[^:]*://
, et au cas où ce caractère pourrait apparaître dans l'entrée , utilisez un mécanisme d'échappement qui ne se heurte pas aux chaînes de début et de fin.Un exemple:
Avec GNU
sed
, une approche consiste à utiliser la nouvelle ligne comme caractère de remplacement. Parce quesed
traite une ligne à la fois, la nouvelle ligne ne se produit jamais dans l'espace de motif, donc on peut faire:Cela ne fonctionne généralement pas avec d'autres
sed
implémentations car elles ne prennent pas en charge[^\n]
. Avec GNU,sed
vous devez vous assurer que la compatibilité POSIX n'est pas activée (comme avec la variable d'environnement POSIXLY_CORRECT).la source
Non, les expressions rationnelles sed n'ont pas de correspondance non gourmande.
Vous pouvez faire correspondre tout le texte jusqu'à la première occurrence de
AC
en utilisant «tout ce qui ne contient pasAC
» suivi deAC
, ce qui fait la même chose que Perl.*?AC
. Le fait est que «tout ce qui ne contient pasAC
» ne peut pas être exprimé facilement comme une expression régulière: il y a toujours une expression régulière qui reconnaît la négation d'une expression régulière, mais l'expression rationnelle de négation se complique rapidement. Et dans sed portable, ce n'est pas possible du tout, car l'expression rationnelle de négation nécessite de grouper une alternance qui est présente dans les expressions régulières étendues (par exemple dans awk) mais pas dans les expressions régulières de base portables. Certaines versions de sed, comme GNU sed, ont des extensions à BRE qui permettent d'exprimer toutes les expressions régulières possibles.En raison de la difficulté de nier une expression régulière, cela ne se généralise pas bien. Ce que vous pouvez faire à la place est de transformer temporairement la ligne. Dans certaines implémentations sed, vous pouvez utiliser des sauts de ligne comme marqueur, car ils ne peuvent pas apparaître dans une ligne d'entrée (et si vous avez besoin de plusieurs marqueurs, utilisez le saut de ligne suivi d'un caractère variable).
Cependant, sachez que backslash-newline ne fonctionne pas dans un jeu de caractères avec certaines versions sed. En particulier, cela ne fonctionne pas dans GNU sed, qui est l'implémentation sed sur Linux non embarqué; dans GNU sed, vous pouvez utiliser à la
\n
place:Dans ce cas précis, il suffit de remplacer le premier
AC
par une nouvelle ligne. L'approche que j'ai présentée ci-dessus est plus générale.Une approche plus puissante dans sed consiste à enregistrer la ligne dans l'espace d'attente, à supprimer tout sauf la première partie «intéressante» de la ligne, à échanger l'espace d'attente et l'espace modèle ou à ajouter l'espace modèle à l'espace d'attente et à répéter. Cependant, si vous commencez à faire des choses aussi compliquées, vous devriez vraiment penser à passer à awk. Awk n'a pas non plus de correspondance non gourmande, mais vous pouvez fractionner une chaîne et enregistrer les parties en variables.
la source
s/\n//g
supprime toutes les nouvelles lignes.sed - correspondance non gourmande par Christoph Sieghart
la source
Dans votre cas, vous pouvez simplement annuler le caractère de fermeture de cette façon:
la source
AB
et la première occurrence deAC
avecXXX
…», et donnessABteAstACABnnACss
comme exemple une entrée. Cette réponse fonctionne pour cet exemple , mais ne répond pas à la question en général. Par exemple,ssABteCstACABnnACss
devrait également produire la sortieaaXXXABnnACss
, mais votre commande passe inchangée sur cette ligne.La solution est assez simple.
.*
est gourmand, mais il n'est pas absolument gourmand. Envisagez une correspondancessABteAstACABnnACss
avec l'expression rationnelleAB.*AC
. CeAC
qui suit.*
doit en fait avoir une correspondance. Le problème est que parce qu'il.*
est gourmand, le suivantAC
correspondra au dernierAC
plutôt qu'au premier..*
mange le premierAC
tandis que le littéralAC
dans l'expression rationnelle correspond au dernier dans ssABteAstACABnn AC ss. Pour éviter que cela ne se produise, remplacez simplement le premierAC
par quelque chose de ridicule pour le différencier du second et de toute autre chose.Le gourmand
.*
va maintenant arrêter au pied de-foobar-
dans ,ssABteAst-foobar-ABnnACss
car il n'y a pas d' autre-foobar-
que cela-foobar-
, et le regexp-foobar-
DOIT avoir un match. Le problème précédent était que l'expression régulièreAC
avait deux correspondances, mais parce qu'elle.*
était gourmande, la dernière correspondance pour aAC
été sélectionnée. Cependant, avec-foobar-
, une seule correspondance est possible, et cette correspondance prouve que ce.*
n'est pas absolument gourmand. L'arrêt de bus pour.*
se produit où il ne reste qu'une correspondance pour le reste de l'expression rationnelle suivante.*
.Notez que cette solution échouera si un
AC
apparaît avant le premierAB
car le mauvaisAC
sera remplacé par-foobar-
. Par exemple, après la premièresed
substitution,ACssABteAstACABnnACss
devient-foobar-ssABteAstACABnnACss
; par conséquent, aucune correspondance ne peut être trouvéeAB.*-foobar-
. Cependant, si la séquence est toujours ... AB ... AC ... AB ... AC ..., alors cette solution réussira.la source
Une alternative est de changer la chaîne pour que vous vouliez la correspondance gourmande
Utilisez
rev
pour inverser la chaîne, inversez vos critères de correspondance, utilisezsed
de la manière habituelle, puis inversez le résultat ....la source