Вывести только N-ю строку перед каждой строкой, которая соответствует шаблону

Вывести только N-ю строку перед каждой строкой, которая соответствует шаблону

Я пытаюсь напечатать только <N>строку th перед шаблоном поиска. grep -B<N>печатает все <N>строки перед шаблоном поиска. Я видел код awkздеськоторый может напечатать только <N>строку после шаблона поиска.

awk 'c&&!--c;/pattern/{c=N}' file

Как изменить это так, чтобы печатать только <N>строку th перед каждой строкой, которая соответствует pattern? Например, вот мой входной файл

...
...
   0.50007496  0.42473932  0.01527831
   0.99997456  0.97033575  0.44364198
Direct configuration=     1
   0.16929051  0.16544726  0.16608723
   0.16984300  0.16855274  0.50171112
...
...
   0.50089841  0.42608090  0.01499159
   0.99982054  0.97154975  0.44403547
Direct configuration=     2
   0.16931296  0.16553376  0.16600890
   0.16999941  0.16847055  0.50170694  
...

Мне нужна команда, которая может вернуть мне 2nd lineпредыдущую строку поиска Direct configuration. Я пытаюсь запустить это вSUSE-Linux

решение1

Необходимо использовать буфер строк.

Попробуйте это:

awk -v N=4 -v pattern="example.*pattern" '{i=(1+(i%N));if (buffer[i]&& $0 ~ pattern) print buffer[i]; buffer[i]=$0;}' file

Установите Nзначение в N-й строке перед шаблоном для печати.

Задайте patternзначение регулярного выражения для поиска.

bufferмассив Nэлементов. Используется для хранения строк. Каждый раз, когда шаблон найден, Nпечатается строка th перед шаблоном.

решение2

Этот код не работает для предыдущих строк. Чтобы получить строки до совпавшего шаблона, вам нужно как-то сохранить уже обработанные строки. Поскольку awkесть только ассоциативные массивы, я не могу придумать столь же простого способа сделать то, что вы хотите в awk, поэтому вот решение на perl:

perl -ne 'push @lines,$_; print $lines[0] if /PAT/; shift(@lines) if $.>LIM;' file 

Измените PATна шаблон, который вы хотите сопоставить, и LIMна количество строк. Например, чтобы напечатать 5-ю строку перед каждым вхождением foo, вы должны запустить:

perl -ne 'push @lines,$_; print $lines[0] if /foo/; shift(@lines) if $.>5;' file 

Объяснение

  • perl -ne: прочитать входной файл построчно и применить заданный скрипт -eк каждой строке.
  • push @lines,$_: добавить текущую строку ( $_) в массив @lines.
  • print $lines[0] if /PAT/: вывести первый элемент массива @lines( $lines[0]), если текущая строка соответствует желаемому шаблону.
  • shift(@lines) if $.>LIM;: $.— это номер текущей строки. Если он больше лимита, удалить первое значение из массива @lines. Результатом будет то, что @linesвсегда будут последние LIMстроки.

решение3

tac file | awk 'c&&!--c;/pattern/{c=N}' | tac

Но здесь есть тот же пробел, что и в случае использования «forward», когда есть несколько совпадений в пределах N строк друг от друга.

И это не будет работать так хорошо, если входные данные передаются из работающего процесса, но это самый простой способ, когда входной файл завершен и не растет.

решение4

Альтернативный способ с sed.

ДляН=1:

sed '$!N; /.*\n.*pattern/P; D' FILE

ДляН=2

sed '1N; $!N; /.*\n.*\n.*pattern/P; D' FILE

ДляН=2случае, 1-я строка это будет читаться следующимН-1строки в пространстве шаблона, затем начать N;P;Dцикл — прочитать еще одну строку и, если последняя строка в пространстве шаблона совпадает, вывести первую строку в пространстве шаблона, затем удалить ее, начиная новый цикл.

Недостатком является то, что его необходимо модифицировать для разных значенийН:

ДляН=3:

sed '1{N;N}; $!N; /.*\n.*\n.*\n.*pattern/P; D' FILE

ДляН=4:

sed '1{N;N;N}; $!N; /.*\n.*\n.*\n.*\n.*pattern/P; D' FILE

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

Связанный контент