Grep a Файл журнала для последнего вхождения строки между двумя строками
У меня есть файл журнала trace.log
. В нем мне нужно grep для содержимого, содержащегося в строках <tag>
и </tag>
. Существует несколько наборов этой пары строк, и мне просто нужно вернуть содержимое между последним набором (другими словами, из tail
файла журнала).
Дополнительный кредит: каким-либо образом я могу вернуть содержимое, содержащееся в двух строках, только если содержимое содержит "testString"?
Спасибо, что посмотрели.
EDIT: параметры поиска и содержатся в разных строках, содержащих около 100 строк содержимого, разделяющих их. Содержание - это то, что мне нужно...
Ответы
Ответ 1
Используйте tac
, чтобы распечатать файл в обратном порядке, а затем grep -m1
, чтобы просто напечатать один результат. Посмотрите вперед и посмотрите вперед текст между <tag>
и </tag>
.
tac a | grep -m1 -oP '(?<=tag>).*(?=</tag>)'
Test
Учитывая этот файл
$ cat a
<tag> and </tag>
aaa <tag> and <b> other things </tag>
adsaad <tag>and last one</tag>
$ tac a | grep -m1 -oP '(?<=tag>).*(?=</tag>)'
and last one
Update
EDIT: параметры поиска и содержатся в разных строках с около 100 строк содержимого, разделяющих их. Содержание - это то, что я после того, как...
Тогда это немного сложнее:
tac file | awk '/<\/tag>/ {p=1; split($0, a, "</tag>"); $0=a[1]};
/<tag>/ {p=0; split($0, a, "<tag>"); $0=a[2]; print; exit};
p' | tac
Идея состоит в том, чтобы перевернуть файл и использовать флаг p
, чтобы проверить, появился ли еще <tag>
или нет. Он начнет печать, когда </tag>
появится и закончится, когда появится <tag>
(потому что мы читаем наоборот).
-
split($0, a, "</tag>"); $0=a[1];
получает данные до </tag>
-
split($0, a, "<tag>" ); $0=a[2];
получает данные после <tag>
Test
Для файла a
выполните следующие действия:
<tag> and </tag>
aaa <tag> and <b> other thing
come here
and here </tag>
some text<tag>tag is starting here
blabla
and ends here</tag>
Выход будет:
$ tac a | awk '/<\/tag>/ {p=1; split($0, a, "</tag>"); $0=a[1]}; /<tag>/ {p=0; split($0, a, "<tag>"); $0=a[2]; print; exit}; p' | tac
tag is starting here
blabla
and ends here
Ответ 2
Если я, как и я, у вас нет доступа к tac, потому что ваш системный администратор не будет играть в мяч, вы можете попробовать:
grep pattern file | tail -1
Ответ 3
Другим решением, чем grep, будет sed:
tac file | sed -n '0,/<tag>\(.*\)<\/tag>/s//\1/p'
tac file
печатает файл в обратном порядке (cat
назад), затем sed
переходит из строки ввода 0
в первое вхождение <tag>.*<\tag>
и заменяет <tag>.*<\tag>
только той частью, которая был внутри <tag>
. Флаг p
печатает вывод, который был подавлен -n
.
Изменить: это не работает, если <tag>
и </tag>
находятся на разных строках. Мы можем использовать sed
для этого:
tac file | sed -n '/<\/tag>/,$p; /<tag>/q' | sed 's/.*<tag>//; s/<\/tag>.*//' | tac
Снова мы используем tac
для чтения файла назад, затем первая команда sed
считывает с первого появления и завершает работу, когда находит. Распечатываются только строки между ними. Затем мы передаем его другому процессу sed
, чтобы разбить и, наконец, снова изменить строки с помощью tac
.
Ответ 4
perl -e '$/=undef; $f=<>; push @a,$1 while($f=~m#<tag>(.*?)</tag>#msg); print $a[-1]' ex.txt
Дополнительный кредит: любым способом я могу вернуть содержимое, содержащееся в две строки, только если содержимое содержит "testString"?
perl -e '$/=undef; $f=<>; push @a,$1 while($f=~m#<tag>(.*?)</tag>#msg); print $a[-1] if ($a[-1]~=/teststring/);' ex.txt
Ответ 5
Маленький непроверенный awk, который обрабатывает несколько строк:
awk '
BEGIN {retain="false"}
/<\tag>/ {retain = retain + $0; keep="false"; next}
/<tag>/ {keep = "true"; retain = $0; next}
keep == "true" {retain = retain + $0}
END {print retain}
' filename
Мы начинаем просто читать файл; Когда мы ударяем, мы начинаем держать линии. Когда мы ударим, остановимся. Если мы ударим другого, мы очистим сохраненную строку и начнем снова. Если вы хотите, чтобы все строки были напечатаны на каждом