6

Как я могу "grep" линии между вхождением некоторой строки1 и (Nth) вхождением некоторой строки2.

например

если файл имеет строку:

A
В
С
D
Е

F
г
В
С
Е

Q

Я хочу выделить строки жирным шрифтом (те, которые начинаются с B и заканчиваются на E).

Можно ли это сделать с помощью grep? или какой-либо другой инструмент командной строки Unix?

2 ответа2

10

grep не очень подходит для этой задачи, вам нужно перейти на один инструмент "вверх":

sed -n '/^B/,/^E/p' infile

Выход:

B
C
D
E
B
C
E

Что касается N-го требования, я думаю, что проще всего, если вы снова продвинете один инструмент "вверх", а именно awk:

awk '/^B/ { f = 1; n++ } f && n == wanted; /^E/ { f = 0 }' wanted=2 infile

Выход:

B
C
E

Флаг f будет установлен при обнаружении /^B/ и сброшен при возникновении /^E/ , почти так же, как работает нотация sed. n отслеживает, сколько блоков прошло, и когда f == 1 && n == wanted имеет значение true, будет выполнен блок по умолчанию ({ print $0 }).

3

Команда sed Тора не может быть побеждена, но с помощью следующего скрипта на perl я пытаюсь ответить на часть вашего вопроса в скобках: «... (Nth) вхождение ...».

Использование:

./script <start-regex> <end-regex> [N]

Примеры с файлом в вашем вопросе:

$ ./script "B" "E" < examplefile
B
C
D
E
B
C
E

$ ./script "B" "E" 2 < examplefile
B
C
D
E
F
G
B
C
E

Нет проверки ошибок или чего бы то ни было, и скрипт не жадный, то есть из A B C D E E F только B C D E будет grep'ed с N = 1.


#!/usr/bin/perl

if ($ARGV[2] != "") { $n = $ARGV[2] } else { $n = 1 }
$begin_str = $ARGV[0];
$end_str = $ARGV[1];

while(<STDIN>) {
  if($_ =~ $begin_str) { $flag=1 }             # beginning of match, set flag    
  if($_ =~ $end_str && $flag eq 1) { $i++ }    # i-th occurence of end string

  if($i eq $n) {                               # end of match after n occurences of end string
    $flag=2;
    $i=0; 
  }

  if ($flag ge 1) {                            # append currrent line to matching part
    $out.=$_;
  }

  if($flag eq 2) {                             # after detection of end of match, print complete match
    print $out;
    # print "---\n";                           # separator after a match
    $out="";
    $flag=0;
  }

}

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .