Соответствующая многострочная строка в командной строке: возвращает определенную строку, если шаблон соответствует, иначе возвращает пустую строку

Вывод команды, которую я имею, принимает следующую форму, когда это "успех":

/ >  -------
ABC123
/ > 

Впрочем, эта команда может выдать что-то вроде этого ("сбой"):

/ >  -------
ABC123
 -------
DEF456
 -------
Hello (world!)
 -------
(any old string, really)
/ > 

Или это (еще один "провал"):

/ > / >

Для первого примера я хотел бы выпустить:

ABC123

Для других двух примеров я хотел бы выдать пустую строку.

Я попробовал это, который отлично работал для третьего примера:

mycmd | pcregrep -M '(?:/\s>\s{2}-{7}\n)[^\n]*(?!\n.*\n)'

Но для первых двух примеров он испустил:

/ >  -------
ABC123

Я в недоумении, что делать. Мое регулярное выражение выше было попыткой соответствовать ведущему / > ------- но не перехватывать его, затем сопоставлять следующую строку, только если за ней не следовала другая строка, заканчивающаяся символом новой строки. Я в порядке, используя что-то, кроме pcregrep чтобы решить эту проблему, но я не могу выразить это с awk или же sed, Я бы использовал Python, но он слишком медленный для моих нужд. Любая помощь?

3 ответа

Решение

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

mycmd | pcregrep -M '(?<=^/ >  -{7}\n).*\n(?=/ > $)'

Но следующие два этапа работали для меня:

mycmd | pcregrep -M '^/ >  -{7}\n.*\n/ > $' | pcregrep -v '^/ >'

Обновление в ответ на ответ ОП

Мне нравится побег \K:-)

Я предполагаю, что вы не хотите соответствовать следующей ситуации

/ > -------
/ > perhaps text here
/ > 

Мне удалось заставить работать отрицательный взгляд вперед, когда он содержит \ n, даже если он встроен в положительный взгляд вперед.

Вот более простое регулярное выражение с \K это ближе к тому, что вы хотите. Запрещает любой контент после / >, но он все еще позволяет линии до / > -------,

mycmd | pcregrep -Mo '^/ >  -{7}\n\K(?!/ >).+(?=\n/ > $(?!\n[\s\S]))'

Если захваченная строка должна быть разрешена для начала с / >тогда проще:

mycmd | pcregrep -Mo '^/ >  -{7}\n\K.+(?=\n/ > $(?!\n[\s\S]))'

Окончательное обновление

Вот один вкладыш sed, который, я считаю, дает точный результат, запрещая любые дополнительные строки до или после. Тем не менее, он позволяет захватить строку, которая начинается с / >,

mycmd | sed -n '1{/^\/ >  -\{7\}$/{n;/./{h;n;/^\/ > $/{${x;p}}}}}'

И вот еще одно решение для sed

mycmd | sed -n '1{h;n;H;x;N;${/^\/ >  -\{7\}\n..*\n\/ > $/{x;p}}}'

Я действительно добился успеха (после большого скрежета зубов) со следующими pcregrep команда:

pcregrep -Mo '^/ > {2}-{7}[\n\r]\K[^\n\r]+(?=[\n\r]/ > $)'

Без -o Отметить, что он включил первую строку (несмотря на использование \K). -o марки pcregrep испускать только те линии, которые соответствуют шаблону. Как выясняется, негативные упреждения, похоже, не работают в многострочном режиме при попытке сопоставления с новыми строками. Также в многострочном режиме \s будет соответствовать переводу строк, поэтому я перестал его использовать.

Я хочу отметить, что ни это решение, ни решение dbenham - это не то, что я хотел. Я надеялся проверить, что после второй строки не было никаких других строк, кроме последней (т. Е. Не содержащей другой новой строки). Эти решения предполагают немного больше о том, как заканчивается вывод, но это нужно сделать.

Вы также можете использовать awk:

BEGIN {
   first_line = "";
   second_line = "";
   third_line = "";

   ctr = 0;
}
{
   if (ctr == 0 ){
      first_line = $0;
   } else if (ctr == 1) {
      second_line = $0;
   } else if (ctr == 2 ) {
      third_line = $0;
   }
   ctr++;
}
END {
   if( first_line ~ /\/ >  -------/){
      if( third_line ~ /\/ >/){
         print second_line;
      }
   }
}

Выход:

$ echo "/ >  -------\nABC12\n ---\n/ >\n" | awk -f test.awk
$ echo "/ >  -------\nABC12\n/ >\n" | awk -f test.awk
ABC12
$

Я уверен, что специалист по awk съежился бы, но это было быстро и сделало свою работу.

Другие вопросы по тегам