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

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

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

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ąć.


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.


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'

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.


1

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

Komenda

sed  '/SomeTestAA/,+1d' filename

wynik

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.