Как выполнить grep -v и исключить следующую строку после матча?


14

Как отфильтровать 2 строки для каждой строки, соответствующей регулярному выражению grep?
это мой минимальный тест:

SomeTestAAAA
EndTest
SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestAABC
EndTest
SomeTestACDF
EndTest

И, очевидно, я пытался, например, grep -vA 1 SomeTestAAкоторый не работает.

желаемый результат:

SomeTestABCD
EndTest
SomeTestDEFG
EndTest
SomeTestACDF
EndTest

grep -v 'SomeTextAA' | уникальный?
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

что за escape-символ для точек? как Some.Test.AA?
Behrooz

1
@Behrooz побег точек \.так grep -P -A 1 'SomeTest\.(?!AA)' file.txtилиgrep -P -A 1 'SomeTest(?!\.AA)' file.txt
heemayl

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

1
@don_crissti это правда, я уже работал над этим.
Behrooz

@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)
Peter.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
Peter.O

3

Одним из вариантов является использование perl- cсовместимого regular expression grep:

pcregrep -Mv 'SomeTestAA.*\n' file

Опция -Mпозволяет шаблону соответствовать более чем одной строке.


1
@don_crissti Обе строки будут удалены. Спецификация OP не распространяется на этот случай.
Джимми

Совершенно очевидно, что пример и вопрос OPs не охватывают такие случаи, мне просто интересно узнать, как это работает (я не знаком с pcre), потому что с нечетным числом последовательных строк, которые совпадают, это работает (удаляет строка контекста тоже) и с четным числом совпадающих последовательных строк, это терпит неудачу (это не удаляет строку контекста после).
don_crissti

Учитывая, что (GNU) grepуже поддерживает PCRE (через -Pопцию), в чем преимущество использования pcregrep?
Ариэльф

@arielf grepне поддерживает -Mпараметр.
Джимми

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

0

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

sed -e '/SomeTestAAAA/,+1d'
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.