Як викреслити -v, а також виключити наступний рядок після матчу?


15

Як відфільтрувати по 2 рядки для кожного рядка, які відповідають греп-регексу?
це мій мінімальний тест:

SomeTestAAAA
EndTest
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestAABC
EndTest
SomeTestACDF
EndTest

І очевидно, я спробував, наприклад, grep -vA 1 SomeTestAAщо не працює.

бажаний вихід:

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

grep -v 'SomeTextAA' | uniq?
DarkHeart

Відповіді:


14

Ви можете використовувати grepз -P(PCRE):

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

(?!AA)- нульова ширина негативного шаблона пошуку, що забезпечує відсутність AAпісля SomeTest.

Тест:

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

який символ втечі для крапок? як Some.Test.AA?
Behrooz

1
@Behrooz Уникнути крапок \.так grep -P -A 1 'SomeTest\.(?!AA)' file.txtчиgrep -P -A 1 'SomeTest(?!\.AA)' file.txt
heemayl

Це працює в даному конкретному випадку, тому що в ОП зразки рядків складаються парами, SomeTest*\nEndTestтак що ви фактично перебираєте grepвсі збіги рядків, SomeTest*але не SomeTestAA+ один рядок контексту після матчу. Додайте ще кілька рядків до вводу (наприклад, додайте рядок foobarпісля кожного EndTestрядка), а потім спробуйте ще раз.
don_crissti

1
@don_crissti це правда, я вже працював над цим.
Бехроз

@Behrooz - хочеш поділитися з нами тим, як ти працював над цим, і, можливо, відповісти на мій коментар під своїм запитанням?
don_crissti

4

Ось sedрішення ( -nбез автоматичної друку), яке працює з довільним введенням:

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

тому з введенням типу

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

біг

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

виходи

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

тобто він видаляє саме ті лінії, які grep -A1 SomeTestAA infileвибрали б:

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

Цікаво. Я не усвідомлював, що //відповідає /SomeTestAA/. Я думав, в цьому випадку, він би відповідав заперечення вираження: /SomeTestAA/!. (+1)
Пітер.O

@ Peter.O - дякую! Ні, за специфікаціями порожній RE завжди повинен відповідати останньому RE, використаному в останній команді; !не є частиною RE , це sedріч.
don_crissti

3

Можливо, вам пощастить із тим, що розглядатиме багаторядкові регіони як єдині записи. Є такий, sgrepякий я мало використовував.

Також є awk, де ви можете встановити роздільник запису вхідних даних і роздільник виводу записів на все, що завгодно.

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

Більшість програми awk є одноцитованими, але я змінюю подвійні лапки в кінці, щоб $patзмінна оболонки могла бути розширена.


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

3

Одним з варіантів є використання pЕрл compatible rРегулярна eXpression grep:

pcregrep -Mv 'SomeTestAA.*\n' file

Цей параметр -Mдозволяє шаблону збігатися більше, ніж один рядок.


1
@don_crissti Обидва рядки буде видалено. Специфікація OP не стосується цієї справи.
jimmij

Це цілком очевидно, що зразок і питання ОП не охоплюють таких випадків, мені просто цікаво дізнатися, як це працює (я не знайомий з pcre), оскільки з непарною кількістю послідовних рядків, які відповідають, це працює (це видаляє рядок контексту також), і однакова кількість послідовних рядків, що відповідають, не виходить (вона не видаляє контекстну лінію після).
don_crissti

З огляду на те, що (GNU) grepвже підтримує PCRE (через -Pопцію), яка перевага використання pcregrep?
аріельф

@arielf grepне підтримує -Mваріант.
jimmij

1

Ви можете використовувати GNU sed«s dкоманди для видалення рядка, і префікс його з /pat/,+Nвибором рядка , відповідної шаблон і наступні N рядків. У вашому випадку N = 1, оскільки ви хочете видалити один наступний рядок після відповідного рядка:

sed -e '/SomeTestAAAA/,+1d'

1

Використовуючи стандарт sed:

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

sedСкрипт аналізує файл рядок за рядком введення, і коли лінія відповідає шаблоном SomeTestAA, дві sedкоманди редагування Nі dвиконуються. NКоманда додає наступний рядок введення в область шаблону (буфер , який sedможе редагувати), і dвидаляє шаблон простір і починає наступний цикл.


1

Спробував команду Below sed, і це спрацювало чудово

командування

sed  '/SomeTestAA/,+1d' filename

вихід

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.