Как получить строки от последнего совпадения до конца файла? - PullRequest
2 голосов
/ 28 мая 2019

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

MARKER
aaa
bbb
ccc
MARKER
ddd
eee
fff
MARKER
ggg
hhh
iii
MARKER
jjj
kkk
lll

Требуется вывод

jjj
kkk
lll

Использую ли я awk с RS и FS для получения желаемого результата?

Ответы [ 4 ]

6 голосов
/ 28 мая 2019

Вы можете сделать это с помощью awk (gawk) без использования трубы.

$ awk -v RS='(^|\n)MARKER\n' 'END{printf "%s", $0}' file
jjj
kkk
lll

Пояснения:

  • Вы определяете свою записьразделитель как (^|\n)MARKER\n через RS='(^|\n)MARKER\n', по умолчанию это EOL char
  • 'END{printf "%s", $0}' => в конце файла, вы печатаете всю строку, так как RS установлен на(^|\n)MARKER\n, $0 будет включать все строки до EOF.


Другой вариант - использовать grep (GNU):
$ grep -zoP '(?<=MARKER\n)(?:(?!MARKER)[^\0])+\Z' file
jjj
kkk
lll

Пояснения:

  • -z для использования символа ASCII NUL в качестве разделителя
  • -o для печати только соответствующих
  • -P вактивировать режим Perl
  • Регулярное выражение PCRE: (?<=MARKER\n)(?:(?!MARKER)[^\0])+\Z объяснено здесь https://regex101.com/r/RpQBUV/2/


И последнее, но не менее важное: также может использоваться следующий подход sed:
sed -n '/^MARKER$/{n;h;b};H;${x;p}' file
jjj
kkk
lll

Пояснения:

  • n перейти к следующей строке
  • h заменить пространство удержания текущей строкой
  • H сделать то же самое, но вместо замены,ppend
  • ${x;p} в конце обмена файлами (x) удерживайте пробел и пробел и печатайте (p)

, которые можно превратить в:

tac file |  sed -n '/^MARKER$/q;p' | tac

, если мы используем tac.

3 голосов
/ 28 мая 2019

Не могли бы вы попробовать следующее.

tac file | awk '/MARKER/{print val;exit} {val=(val?val ORS:"")$0}' | tac

Преимущество этого подхода будет awk будет просто читать последний блок файла Input_file (который будет фактически первым блоком для awk после tacпечатает его в обратном порядке) и после этого выходит.

Объяснение:

tac file |                      ##Printing Input_file in reverse order.
awk '
  /MARKER/{                     ##Searching for a string MARKER in a line of Input_file.
    print val                   ##Printing variable val here. Because we need last occurrence of string MARKER,which has become first instance after reversing the Input_file.
    exit                        ##Using exit to exit from awk program itself.
  }
  {
    val=(val?val ORS:"")$0      ##Creating variable named val whose value will be keep appending to its own value with a new line to get values before string MARKER as per OP question.
  }
' |                             ##Sending output of awk command to tac again to make it in its actual form, since tac prints it in reverse order. 
tac                             ##Using tac to make it in correct order(lines were reversed because of previous tac).
1 голос
/ 28 мая 2019

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

sed -nz 's/.*MARKER.//p' file

При этом используется жадность для удаления всех строк до и включая последнее вхождение MARKER.

0 голосов
/ 29 мая 2019

Вы также можете попробовать Perl

$ perl -0777 -ne ' /.*MARKER(.*)/s and print $1 ' input.txt

jjj
kkk
lll

$
...