Удалить строку, содержащую определенную строку и следующую строку

71

Я использую это

cat foo.txt | sed '/bar/d'

удалить строки, содержащие строку barв файле.

Однако я хотел бы удалить эти строки и строку сразу после нее . Предпочтительно в sed, awkили другой инструмент , который доступен в Mingw32.

Это своего рода реверс , что я могу получить в grepс -Aи -Bнапечатать соответствующие линии, а также линию до / после согласованной линии.

Есть ли простой способ добиться этого?

jakub.g
источник
2
Просто для информации: я анализирую логи, в которых записи двухстрочные. Поэтому я хочу найти запись, соответствующую шаблону, и удалить ее, а также следующую строку. Следовательно, мне не нужно обрабатывать последовательные строки совпадений, но в любом случае спасибо за полноту ваших ответов!
jakub.g

Ответы:

75

Если у вас GNU sed (то есть не встроенный Linux или Cygwin):

sed '/bar/,+1 d'

Если у вас barдве последовательные строки, вторая строка будет удалена без анализа. Например, если у вас есть 3-строчный файл bar/ bar/ foo, fooстрока останется.

Жиль "ТАК - перестань быть злым"
источник
1
+1 для длины :) В моем конкретном примере у меня нет последовательных bars, поэтому этот супер легко запомнить.
jakub.g
11
sed '/bar/d'если вы просто хотите «Удалить строку, содержащую определенную строку», а не следующую.
AJP
Если я хочу удалить все строки после математики тогда?
Пандя
1
@Pandya Это другое. Вы можете использовать, например,sed '/math/q'
Жиль "ТАК - перестать быть злым"
1
@AK Если вы просто хотите удалить совпадающую строку, это еще проще:sed '/bar/d'
Жиль "ТАК - Хватит быть злым"
16

Если это barможет произойти в последовательных строках, вы можете сделать:

awk '/bar/{n=2}; n {n--; next}; 1' < infile > outfile

который может быть адаптирован для удаления более 2 строк, заменяя 2 выше количество строк для удаления, включая соответствующую.

Если нет, это легко сделать sedс помощью решения @MichaelRollins или:

sed '/bar/,/^/d' < infile > outfile
Стефан Шазелас
источник
Другим плюсом в решении AWK является то, что я могу заменить /bar/на /bar|baz|whatever/. В sedэтом синтаксисе, кажется, не работает.
jakub.g
@ jakub.g, у меня есть GNU sed (v4.4 сейчас). Не уверен насчет других. Что я знаю, так это то, что он использует «базовый» синтаксис регулярных выражений по умолчанию, поэтому ваш пример не сработал. Чтобы достичь желаемого, вы можете поставить обратную косую черту перед каждой вертикальной линией или попросить sedиспользовать «расширенные» регулярные выражения. Более подробная информация здесь: gnu.org/software/sed/manual/html_node/… . Обратите внимание, что это относится и к grep. Вот мой собственный рабочий пример: echo $'0a\n1b\n2c' | sed '/0a\|1b/d'.
Виктор Ярема
12

Я не владею sed, но в awk это легко сделать:

awk '/bar/{getline;next} 1' foo.txt 

Скрипт awk гласит: для строки, содержащей bar, получите следующую строку (getline), затем пропустите всю последующую обработку (next). 1 шаблон в конце печатает оставшиеся строки.

Обновить

Как указано в комментарии, вышеуказанное решение не работает с последовательным bar. Вот пересмотренное решение, которое принимает его во внимание:

awk '/bar/ {while (/bar/ && getline>0) ; next} 1' foo.txt 

Теперь мы продолжаем читать, чтобы пропустить все строки / bar /.

Хай вю
источник
1
Чтобы повторить grep -A100%, вам также нужно правильно обрабатывать любое количество последовательных barстрок (удаляя весь блок и 1 строку после).
jw013
7

Вы захотите использовать возможности сценариев sed для достижения этой цели.

$ sed -e '/bar/ { 
 $!N
 d
 }' sample1.txt

Пример данных:

$ cat sample1.txt 
foo
bar
biz
baz
buz

Команда «N» добавляет следующую строку ввода в пространство шаблона. В сочетании со строкой из сопоставления с образцом (/ bar /) будут строки, которые вы хотите удалить. Затем вы можете нормально удалить с помощью команды "d".

Майкл Роллинс
источник
Как мне ввести новую строку в консоли? Или это только сценарий?
jakub.g
@ jakub.g: с помощью GNU sed:sed -e '/bar/{N;d}' sample1.txt
Сайрус
2

Если какая-либо строка, следующая непосредственно за совпадением, должна быть удалена, то ваша sedпрограмма должна будет рассмотреть последовательные совпадения. Другими словами, если вы удаляете строку после совпадения, которое также совпадает, то, вероятно, вам следует удалить также и строку, следующую за этим.

Это реализовано достаточно просто - но вам придется немного оглянуться назад.

printf %s\\n     0 match 2 match match \
                 5 6 match match match \
                 10 11 12 match 14 15  |
sed -ne'x;/match/!{g;//!p;}'

0
6
11
12
15

Он работает путем замены мест удержания и шаблонов для каждой прочитанной строки, поэтому последнюю строку можно сравнивать с текущей каждый раз. Поэтому, когда sedчитает строку, она обменивается содержимым своих буферов - и предыдущая строка затем является содержимым буфера редактирования, а текущая строка помещается в удерживающее пространство.

Поэтому sedпроверяет предыдущую строку на соответствие matchи, если она !не найдена , запускаются два выражения в {функции }. sedбудет gперезаписывать пространство удержания, перезаписывая пространство образца - это означает, что текущая строка будет находиться как в пространстве удержания, так и в пространстве образца - и затем будет //проверять его на соответствие его недавно скомпилированному регулярному выражению match- и если это не matchтак является printed.

Это означает, что строка печатается только в том случае, если это не так, а в предыдущей строке нет . Это также исключает любые ненужные замены для последовательностей es.match matchmatch

Если вы хотите версию, которая может отбрасывать произвольное количество строк, встречающихся после a match, потребуется немного больше работы:

printf %s\\n    1 2 3 4 match  \
                match match 8  \
                9 10 11 12 13  \
                14 match match \
                17 18 19 20 21 |
sed -net -e'/match/{h;n;//h;//!H;G;s/\n/&/5;D;}' -ep

... замените 5 на количество строк (включая совпавшую строку), которые вы хотите удалить ...


1
2
3
4
12
13
14
21
mikeserv
источник