Как выбрать линии между двумя шаблонами маркеров, которые могут встречаться несколько раз с помощью awk/sed

С помощью awk или же sed Как я могу выбрать линии, которые встречаются между двумя различными шаблонами маркеров? Может быть несколько разделов, помеченных этими шаблонами.

Например: предположим, что файл содержит:

abc
def1
ghi1
jkl1
mno
abc
def2
ghi2
jkl2
mno
pqr
stu

И стартовая модель abc и окончание картины mnoИтак, мне нужен вывод как:

def1
ghi1
jkl1
def2
ghi2
jkl2

Я использую sed, чтобы соответствовать шаблону один раз:

sed -e '1,/abc/d' -e '/mno/,$d' <FILE>

Есть ли способ в sed или же awk сделать это повторно до конца файла?

10 ответов

Решение

Использование awk с флажком для запуска печати при необходимости:

$ awk '/abc/{flag=1;next}/mno/{flag=0}flag' file
def1
ghi1
jkl1
def2
ghi2
jkl2

Как это работает?

  • /abc/ соответствует строкам, содержащим этот текст, а также /mno/ делает.
  • /abc/{flag=1;next} устанавливает flag когда текст abc найден. Затем он пропускает строку.
  • /mno/{flag=0} снимает flag когда текст mno найден.
  • Финал flag шаблон с действием по умолчанию, которое заключается в print $0: если flag равен 1, строка печатается.

Более подробное описание и примеры вместе со случаями, когда шаблоны либо показаны, либо нет, см. В разделе Как выбрать линии между двумя шаблонами?,

С помощью sed:

sed -n -e '/^abc$/,/^mno$/{ /^abc$/d; /^mno$/d; p; }'

-n опция означает не печатать по умолчанию.

Шаблон ищет строки, содержащие только abc чтобы просто mnoи затем выполняет действия в { ... }, Первое действие удаляет abc линия; вторая mno линия; и p печатает оставшиеся строки. Вы можете расслабить регулярные выражения по мере необходимости. Любые линии за пределами диапазона abc..mno просто не печатаются.

Это может работать для вас (GNU sed):

sed '/^abc$/,/^mno$/{//!b};d' file

Удалить все строки, кроме строк, начинающихся с abc а также mno

Из ссылок предыдущего ответа, тот, который сделал это для меня, запустив ksh на Solaris, был таким:

sed '1,/firstmatch/d;/secondmatch/,$d'
sed '/^abc$/,/^mno$/!d;//d' file

Гольф на два персонажа лучше, чем у Ппотонга {//!b};d

Пустые косые черты // означают: "повторно использовать последнее использованное регулярное выражение". и команда делает то же самое, что и более понятное:

sed '/^abc$/,/^mno$/!d;/^abc$/d;/^mno$/d' file

Это похоже на POSIX:

Если RE пусто (т. Е. Шаблон не указан), sed должен вести себя так, как если бы было указано последнее RE, использованное в последней примененной команде (либо в качестве адреса, либо в качестве части замещающей команды).

Как -то так у меня работает

file.awk:

BEGIN {
    record=0
}

/^abc$/ {
    record=1
}

/^mno$/ {
    record=0;
    print "s="s;
    s=""
}

!/^abc|mno$/ {
    if (record==1) {
        s = s"\n"$0
    }   
}

с помощью: awk -f file.awk data...

Отредактируйте: решение O_o fedorqui намного лучше / красивее, чем мое.

Don_crissti ответ от Показать только текст между 2 соответствующими шаблонами?

firstmatch="abc"
secondmatch="cdf"
sed "/$firstmatch/,/$secondmatch/!d;//d" infile

который намного эффективнее, чем приложение AWK, смотрите здесь.

perl -lne 'print if((/abc/../mno/) && !(/abc/||/mno/))' your_file

Это также можно сделать с помощью логических операций и операций увеличения/уменьшения флага:

      awk '/mno/&&--f||f||/abc/&&f++' file

Я пытался использовать awk печатать линии между двумя рисунками, в то время как pattern2 также соответствует pattern1. И линия pattern1 также должна быть напечатана.

например источник

package AAA
aaa
bbb
ccc
package BBB
ddd
eee
package CCC
fff
ggg
hhh
iii
package DDD
jjj

должен иметь выход

package BBB
ddd
eee

Где pattern1 package BBBpattern2 - это package \w*, Обратите внимание, что CCC не известное значение, поэтому не может быть в буквальном смысле.

В этом случае ни @scai awk '/abc/{a=1}/mno/{print;a=0}a' file ни @fedorqui 's awk '/abc/{a=1} a; /mno/{a=0}' file работает для меня.

Наконец, мне удалось решить awk '/package BBB/{flag=1;print;next}/package \w*/{flag=0}flag' fileхаха

Немного больше усилий приведет к awk '/package BBB/{flag=1;print;next}flag;/package \w*/{flag=0}' file, для печати строки pattern2, то есть

package BBB
ddd
eee
package CCC
Другие вопросы по тегам