Wie grep -v und auch die nächste Zeile nach dem Match ausschließen?

14

Wie filtere ich 2 Zeilen für jede Zeile, die zum regulären Ausdruck grep passt?
Das ist mein minimaler Test:

SomeTestAAAA
EndTest
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestAABC
EndTest
SomeTestACDF
EndTest

Und natürlich habe ich zB probiert grep -vA 1 SomeTestAAwas nicht geht.

gewünschte Ausgabe ist:

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest
Behrooz
quelle
grep -v 'SomeTextAA' | uniq?
DarkHeart

Antworten:

14

Sie können grepmit -P(PCRE) verwenden:

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

(?!AA)ist die Null gewährleistet negative Vorgriffs - Musterbreite , dass es keine AAnach SomeTest.

Prüfung :

$ grep -P -A 1 'SomeTest(?!AA)' file.txt 
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest
heemayl
quelle
Was ist das Fluchtzeichen für Punkte? wie Some.Test.AA?
Behrooz
1
@Behrooz Escape Dots von \.so grep -P -A 1 'SomeTest\.(?!AA)' file.txtodergrep -P -A 1 'SomeTest(?!\.AA)' file.txt
Heemayl
Dies funktioniert in diesem speziellen Fall, da in OPs die Beispielzeilen paarweise SomeTest*\nEndTestvorliegen, sodass Sie tatsächlich grepalle übereinstimmenden Zeilen anpingen, SomeTest*jedoch nicht SomeTestAA+ eine Kontextzeile nach der Übereinstimmung. Fügen Sie der Eingabe weitere Zeilen hinzu (z. B. fügen Sie foobarnach jeder EndTestZeile eine Zeile hinzu), und wiederholen Sie den Vorgang.
don_crissti
1
@don_crissti das stimmt, das habe ich schon umgangen.
Behrooz
@Behrooz - möchten Sie uns mitteilen, wie Sie damit umgegangen sind, und vielleicht meinen Kommentar unter Ihrer Frage beantworten?
don_crissti
4

Hier ist eine sedLösung ( -nohne automatisches Drucken), die mit beliebigen Eingaben funktioniert:

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

also mit einem eingang wie

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

Laufen

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

Ausgänge

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

Das heißt, es werden genau die Zeilen entfernt, grep -A1 SomeTestAA infiledie ausgewählt würden:

SomeTestAAXX
SomeTestAAYY
+ one line
--
SomeTestAABC
+ another line
--
SomeTestAA
+ yet another line
don_crissti
quelle
Interessant. Ich habe nicht bemerkt, dass das //passt /SomeTestAA/. Ich dachte, in diesem Fall würde es den negierten Ausdruck abgestimmt hat: /SomeTestAA/!. (+1)
Peter.O
@ Peter.O - danke! Nein, laut Spezifikation sollte eine leere RE immer mit der letzten RE übereinstimmen, die im letzten Befehl verwendet wurde. Das !ist nicht Teil des RE , es ist eine sedSache.
don_crissti
3

Vielleicht haben Sie mehr Glück mit etwas, das mehrzeilige Regionen als einzelne Datensätze betrachtet. Es gibt eine, sgrepdie ich nicht viel benutzt habe.

Es gibt auch awk, wo Sie das Trennzeichen für die Eingabe und die Ausgabe von Datensätzen nach Belieben einstellen können.

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

Der größte Teil des awk-Programms steht in einfachen Anführungszeichen, aber am Ende werden doppelte Anführungszeichen verwendet, damit die $patShell-Variable erweitert werden kann.

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

Eine Möglichkeit ist die Verwendung perl ckompatibler regularer eAusdrücke grep:

pcregrep -Mv 'SomeTestAA.*\n' file

Mit dieser Option -Mkann das Muster mit mehr als einer Zeile übereinstimmen.

jimmij
quelle
1
@don_crissti Beide Zeilen werden entfernt. Die OP-Spezifikation deckt diesen Fall nicht ab.
Jimmy
Es ist ziemlich offensichtlich, dass das OP-Beispiel & die Frage solche Fälle nicht abdecken. Ich bin nur gespannt, wie dies funktioniert (ich kenne mich mit pcre nicht aus), da dies bei einer ungeraden Anzahl aufeinanderfolgender übereinstimmender Zeilen funktioniert (es wird entfernt) Auch die Kontextzeile) und eine gerade Anzahl aufeinanderfolgender übereinstimmender Zeilen schlagen fehl (die Kontextzeile danach wird nicht entfernt).
don_crissti
Angesichts dessen, dass (GNU) grepPCRE (über die -POption ) bereits unterstützt , was ist der Vorteil der Verwendung pcregrep?
Arielf
@arielf grepunterstützt keine -MOption.
Jimmy
1

Mit standard sed:

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

Das sedSkript analysiert die Eingabedatei Zeile für Zeile, und wenn eine Linie des Muster übereinstimmt SomeTestAA, die beiden sedBearbeitungsbefehle Nund dausgeführt werden. Der NBefehl hängt die nächste Eingabezeile an den Musterbereich (den Puffer, der sedbearbeitet werden kann) an, dlöscht den Musterbereich und startet den nächsten Zyklus.

Kusalananda
quelle
0

Sie können den Befehl von GNU verwenden sed, dum eine Zeile zu löschen und mit einem Präfix zu versehen /pat/,+N, um Zeilen auszuwählen, die dem Muster und den folgenden N Zeilen entsprechen. In Ihrem Fall ist N = 1, da Sie nur die einzelne nachfolgende Zeile nach einer übereinstimmenden Zeile löschen möchten:

sed -e '/SomeTestAAAA/,+1d'
Kaktus
quelle
0

Versucht mit Below sed Befehl und es hat gut funktioniert

Befehl

sed  '/SomeTestAA/,+1d' filename

Ausgabe

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest
Praveen Kumar BS
quelle