Jak grep -v, a także wykluczyć następną linię po meczu?

15

Jak odfiltrować 2 linie dla każdej linii pasującej do wyrażenia regularnego grep?
to mój minimalny test:

SomeTestAAAA
EndTest
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestAABC
EndTest
SomeTestACDF
EndTest

I oczywiście próbowałem np. grep -vA 1 SomeTestAAKtóre nie działa.

pożądana wydajność to:

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest
Behrooz
źródło
grep -v 'SomeTextAA' | uniq?
DarkHeart

Odpowiedzi:

14

Możesz używać grepz -P(PCRE):

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

(?!AA)jest ujemnym wzorem wyprzedzającym o zerowej szerokości, zapewniającym, że nie będzie AApo nim SomeTest.

Test:

$ grep -P -A 1 'SomeTest(?!AA)' file.txt 
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest
heemayl
źródło
jaki jest znak ucieczki dla kropek? jak Some.Test.AA?
Behrooz
1
@ Behrooz Escape kropki przez \.so grep -P -A 1 'SomeTest\.(?!AA)' file.txtlubgrep -P -A 1 'SomeTest(?!\.AA)' file.txt
heemayl
Działa to w tym konkretnym przypadku, ponieważ w PO próbne linie występują w parach, SomeTest*\nEndTestdzięki czemu można pingować grepwszystkie pasujące linie, SomeTest*ale nie SomeTestAA+ jedna linia kontekstu po dopasowaniu. Dodaj więcej linii do wejścia (np. Dodaj linię foobarpo każdej EndTestlinii), a następnie spróbuj ponownie.
don_crissti
1
@don_crissti to prawda, już nad tym pracowałem.
Behrooz
@ Behrooz - chciałbyś podzielić się z nami tym, jak sobie z tym poradziłeś i być może odpowiedzieć na mój komentarz pod twoim pytaniem?
don_crissti
4

Oto sedrozwiązanie ( -nbez automatycznego drukowania), które działa z dowolnymi danymi wejściowymi:

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

więc z wejściem jak

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

bieganie

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

wyjścia

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

to znaczy usuwa dokładnie linie, grep -A1 SomeTestAA infilektóre wybrałyby:

SomeTestAAXX
SomeTestAAYY
+ one line
--
SomeTestAABC
+ another line
--
SomeTestAA
+ yet another line
don_crissti
źródło
Ciekawy. Nie zdawałem sobie sprawy, że //dopasowane /SomeTestAA/. Myślałem, że w tym przypadku byłoby to pasowało do zanegowane wyrażenie: /SomeTestAA/!. (+1)
Peter.O,
@ Peter.O - dzięki! Nie, zgodnie ze specyfikacją, puste RE powinno zawsze pasować do ostatniego RE użytego w ostatnim poleceniu; !nie jest częścią RE , jest to sedsprawa.
don_crissti
3

Możesz mieć więcej szczęścia z czymś, co wygląda na regiony wieloliniowe jako pojedyncze rekordy. Jest taki, z sgrepktórego nie korzystałem wiele.

Istnieje również awk, w którym możesz ustawić separator rekordów wejściowych i separator rekordów wyjściowych na cokolwiek zechcesz.

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

Większość programu awk jest pojedynczego cudzysłowu, ale zmieniam na podwójne cudzysłowy na końcu, aby $patzmienną powłoki można było rozwinąć.

Peter Cordes
źródło
awk -vpat="^SomeTestAA" -vRS="\nEndTest\n" 'BEGIN{ ORS=RS } $0 !~ pat' file
Peter.O,
3

Jedną z opcji jest użycie pErl compatible regular eXpression grep:

pcregrep -Mv 'SomeTestAA.*\n' file

Ta opcja -Mpozwala dopasować wzór do więcej niż jednej linii.

jimmij
źródło
1
@don_crissti Obie linie zostaną usunięte. Specyfikacja OP nie obejmuje tego przypadku.
jimmij
To oczywiste, że próbka OP i pytanie nie obejmują takich przypadków, jestem ciekawy, jak to działa (nie znam pcre), ponieważ przy nieparzystej liczbie kolejnych pasujących wierszy, to działa (usuwa linia kontekstu też) i przy parzystej liczbie kolejnych pasujących linii, nie działa (nie usuwa linii kontekstu później).
don_crissti
Biorąc pod uwagę, że (GNU) grepjuż obsługuje PCRE (poprzez -Popcję), jaka jest korzyść z używania pcregrep?
arielf
@arielf grepnie obsługuje -Mopcji.
jimmij
1

Możesz użyć polecenia GNU sed, daby usunąć linię i poprzedzić ją znakiem, /pat/,+Naby wybrać linie pasujące do wzorca i kolejnych N linii. W twoim przypadku N = 1, ponieważ chcesz usunąć tylko jedną kolejną linię po pasującej linii:

sed -e '/SomeTestAAAA/,+1d'
Kaktus
źródło
1

Za pomocą standardowego sed:

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

sedSkrypt analizuje wiersz po wierszu pliku wejściowego, a gdy linia pasuje do wzorca SomeTestAA, dwa sedpolecenia edycji Ni dsą wykonywane. NPolecenie dołącza następną linię wejścia do przestrzeni wzorca (buforze tym sedmożna edytować) i dusuwa przestrzeń wzór i rozpoczyna następny cykl.

Kusalananda
źródło
1

Wypróbowałem komendę Below sed i działało dobrze

Komenda

sed  '/SomeTestAA/,+1d' filename

wynik

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest
Praveen Kumar BS
źródło