Получить последнее вхождение шаблона перед другим шаблоном

Получить последнее вхождение шаблона перед другим шаблоном

В файле вроде этого:

...
Pattern2:TheWrongBar
foo 
Pattern2:TheRightBar
foo 
First Pattern
foo
...

Мне нужно найти последнее вхождение Pattern2этого слова перед First Patternтем, что в данном случае будетPattern2:TheRightBar

Моя первая идея — получить все оставшиеся файлы First patternс помощью:

sed -e '/First Pattern/,$d' myfile | tac | grep -m1 "Pattern I need to get"

Неужели нет способа оптимизировать этот код?

решение1

С awk:

awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file.txt
  • /Pattern2/ {line=$0; next}: Если шаблон Pattern2совпадает, сохраняем строку в переменной lineи переходим к следующей строке

  • /First Pattern/ {print line; exit}: если First Patternнайдено, выводим переменную lineи выходим

Пример:

% cat file.txt                                                                 
...
Pattern2:TheWrongBar
foo 
Pattern2:TheRightBar
foo 
First Pattern
foo
...

% awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file.txt
Pattern2:TheRightBar

решение2

Ты мог бы бежать

sed '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/!d;q' infile

Как это работает:

sed '/PATTERN2/h         # if line matches PATTERN2 save it to hold buffer 
/PATTERN1/!d             # if it doesn't match PATTERN1 delete it
x                        # exchange buffers
/PATTERN2/!d             # if current pattern space doesn't match delete it
q' infile                # quit (auto-printing the current pattern space)

Это приведет к выходу только в том случае, если есть хотя бы одна совпадающая строка PATTERN2перед какой-либо совпадающей строкой, PATTERN1поэтому при вводе типа

1
2
PATTERN1
PATTERN2--1st
3
PATTERN2--2nd
PATTERN1
...

он будет печатать

PATTERN2--2nd

Если бы вы хотели выйти при первом совпадении PATTERN1независимо от этого, вы бы запустили

sed -n '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/p;q' infile

который ничего не выводит с входными данными выше (это решение делает ровно то же самое, что и ваше).

решение3

Находит количество строк «Первого шаблона», затем использует head для отображения строк над ним, передает через tac и grep.

head --lines=+"$(grep -nm1 "First Pattern" file | cut -d\: -f1)" file | tac | grep -m1 "Pattern2" 

Например.

head --lines=+6 file | tac | grep -m1 "Pattern2" 

Это надежнее, чем использование -m 1000000 в grep. Поскольку скорость важна для OP, я проверил время выполнения, и оно также, похоже, быстрее, чем все остальные текущие ответы (на моей системе)

wc -l file
25910209 file

time awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file
Pattern2:TheRightBar

real  0m2.881s
user  0m2.844s
sys 0m0.036s

time sed '/Pattern2/h;/First Pattern/!d;x;/Pattern2/!d;q' file
Pattern2:TheRightBar

real  0m5.218s
user  0m5.192s
sys 0m0.024s

time (grep -m1 "First Pattern" file -B 10000000 | tac | grep -m1 "Pattern2")

real  0m0.624s
user  0m0.552s
sys 0m0.124s

time (head --lines=+"$(grep -nm1 "First Pattern" file | cut -d\: -f1)" file | tac | grep -m1 "Pattern2")
Pattern2:TheRightBar

real  0m0.586s
user  0m0.528s
sys 0m0.160s

решение4

Оказывается, самый эффективный способв моем случаебыл:

grep -m1 "First Pattern" my_file -B 10000000 | tac | grep -m1 "Pattern2"

Очевидно, что -Bв некоторых примерах этот вариант использовать нельзя, но grepон намного быстрее, чем awkили sed, что я выбрал для этого решения. Если значение параметра -Bстановится выше, поиск становится намного менее эффективным.

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