Видаліть рядок, що містить певний рядок, і наступний рядок


70

Я цим користуюся

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

для видалення рядків, що містять рядок barу файлі.

Однак я хотів би видалити ці рядки та лінію прямо після неї . Переважно в sed, awkабо іншому інструменті, доступному в MinGW32.

Це свого роду реверс , що я можу отримати в grepс -Aі -Bнадрукувати відповідні лінії, а також лінію до / після узгодженої лінії.

Чи є якийсь простий спосіб досягти цього?


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

Відповіді:


74

Якщо у вас є 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 '/bar/{getline;next} 1' foo.txt 

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

Оновлення

Як зазначалося в коментарі, вищезазначене рішення не працювало послідовно bar. Ось переглянуте рішення, яке враховує:

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

Тепер ми продовжуємо читати, щоб пропустити всі / бар / рядки.


1
Щоб повторити grep -A100%, вам також потрібно правильно обробити будь-яку кількість послідовних barрядків (видаливши весь блок і 1 рядок після).
jw013

7

Ви хочете скористатися можливостями сценаріїв sed для цього.

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

Приклад даних:

$ cat sample1.txt 
foo
bar
biz
baz
buz

Команда "N" додає наступний рядок введення в простір шаблону. Це в поєднанні з рядком із збігу шаблонів (/ бар /) будуть рядками, які ви бажаєте видалити. Потім можна нормально видалити команду "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

Якщо ви хочете, щоб версія, яка могла випустити довільну кількість рядків, що виникають після 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.