Comment grep -v et exclure également la ligne suivante après le match?


14

Comment filtrer 2 lignes pour chaque ligne correspondant à l'expression rationnelle grep?
c'est mon test minimal:

SomeTestAAAA
EndTest
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestAABC
EndTest
SomeTestACDF
EndTest

Et évidemment j'ai essayé par exemple grep -vA 1 SomeTestAAce qui ne fonctionne pas.

la sortie souhaitée est:

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

grep -v 'SomeTextAA' | uniq?
DarkHeart

Réponses:


14

Vous pouvez utiliser grepavec -P(PCRE):

grep -P -A 1 'SomeTest(?!AA)' file.txt

(?!AA)est le motif d'anticipation négatif de largeur nulle garantissant qu'il n'y a pas d' AAaprès SomeTest.

Test:

$ grep -P -A 1 'SomeTest(?!AA)' file.txt 
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

quel est le caractère d'échappement pour les points? comme Some.Test.AA?
Behrooz

1
@Behrooz Escape dots by \.so grep -P -A 1 'SomeTest\.(?!AA)' file.txtorgrep -P -A 1 'SomeTest(?!\.AA)' file.txt
heemayl

Cela fonctionne dans ce cas particulier, car dans les OP, les exemples de lignes viennent par paires SomeTest*\nEndTest, vous effectuez donc une commande grepping sur toutes les lignes correspondantes, SomeTest*mais pas SomeTestAA+ une ligne de contexte après la correspondance. Ajoutez quelques lignes supplémentaires à l'entrée (par exemple, ajoutez une ligne foobaraprès chaque EndTestligne), puis réessayez.
don_crissti

1
@don_crissti c'est vrai, j'ai déjà travaillé autour de ça.
Behrooz

@ Behrooz - voulez-vous partager avec nous comment vous avez travaillé autour de cela et peut-être répondre à mon commentaire sous votre question?
don_crissti

4

Voici une sedsolution (sans -naucune impression automatique) qui fonctionne avec une entrée arbitraire:

sed -n '/SomeTestAA/!p          # if line doesn't match, print it
: m                             # label m
//{                             # if line matches
$!{                             # and if it's not the last line
n                               # empty pattern space and read in the next line
b m                             # branch to label m (so n is repeated until a
}                               # line that's read in no longer matches) but
}                               # nothing is printed
' infile

donc avec une entrée comme

SomeTestAAXX
SomeTestAAYY
+ one line
SomeTestONE
Message body
EndTest
########
SomeTestTWO
something here
EndTest
SomeTestAABC
+ another line
SomeTestTHREE
EndTest
SomeTestAA
+ yet another line

fonctionnement

sed -n -e '/SomeTestAA/!p;: m' -e '//{' -e '$!{' -e 'n;b m' -e '}' -e'}' infile

les sorties

SomeTestONE
Message body
EndTest
########
SomeTestTWO
something here
EndTest
SomeTestTHREE
EndTest

c'est-à-dire qu'il supprime exactement les lignes qui grep -A1 SomeTestAA infilesélectionneraient:

SomeTestAAXX
SomeTestAAYY
+ one line
--
SomeTestAABC
+ another line
--
SomeTestAA
+ yet another line

Intéressant. Je ne savais pas que cela //correspondait /SomeTestAA/. Je pensais que , dans ce cas, il aurait assorti l'expression niée: /SomeTestAA/!. (+1)
Peter.O

@ Peter.O - merci! Non, selon les spécifications, un RE vide doit toujours correspondre au dernier RE utilisé dans la dernière commande; le !ne fait pas partie du RE , il est une sedchose.
don_crissti

3

Vous pourriez avoir plus de chance avec quelque chose qui considère les régions multilignes comme des enregistrements uniques. Il y en a un sgrepque je n'ai pas beaucoup utilisé.

Il y a aussi awk, où vous pouvez définir le séparateur d'enregistrements d'entrée et le séparateur d'enregistrements de sortie, comme vous le souhaitez.

pat="^SomeTestAA"
awk  'BEGIN{ RS=ORS="\nEndTest\n"} !/'"$pat/" foo

La plupart du programme awk est entre guillemets simples, mais je passe à des guillemets doubles à la fin afin que la $patvariable shell puisse être développée.


awk -vpat="^SomeTestAA" -vRS="\nEndTest\n" 'BEGIN{ ORS=RS } $0 !~ pat' file
Peter.O

3

Une option consiste à utiliser une pexpression cegulaire rcompatible avec eerl grep:

pcregrep -Mv 'SomeTestAA.*\n' file

L'option -Mpermet au modèle de correspondre à plus d'une ligne.


1
@don_crissti Les deux lignes seront supprimées. La spécification OP ne couvre pas ce cas.
jimmij

Il est assez évident que l'échantillon et la question OP ne couvrent pas de tels cas, je suis juste curieux de savoir comment cela fonctionne (je ne suis pas familier avec pcre) car avec un nombre impair de lignes consécutives qui correspondent, cela fonctionne (il supprime la ligne de contexte aussi) et avec un nombre pair de lignes consécutives qui correspondent, il échoue (il ne supprime pas la ligne de contexte après).
don_crissti

Étant donné que (GNU) grepprend déjà en charge PCRE (via l' -Poption), quel est l'avantage d'utiliser pcregrep?
arielf

@arielf grepne prend pas en charge l' -Moption.
jimmij

1

En utilisant la norme sed:

$ sed '/SomeTestAA/{ N; d; }' file
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

Le sedscénario analyse la ligne du fichier d'entrée par ligne, et lorsqu'une ligne correspond au motif SomeTestAA, les deux sedcommandes d'édition Net dsont exécutées. La Ncommande ajoute la ligne d'entrée suivante à l'espace modèle (le tampon qui sedpeut être modifié), dsupprime l'espace modèle et démarre le cycle suivant.


1

Essayé avec la commande ci-dessous sed et cela a bien fonctionné

commander

sed  '/SomeTestAA/,+1d' filename

production

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

0

Vous pouvez utiliser sedla dcommande GNU pour supprimer une ligne et la préfixer avec /pat/,+Npour sélectionner les lignes correspondant au modèle et les N lignes suivantes. Dans votre cas, N = 1 car vous ne souhaitez supprimer que la ligne suivante unique après une ligne correspondante:

sed -e '/SomeTestAAAA/,+1d'
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.