В файле вроде этого:
...
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
становится выше, поиск становится намного менее эффективным.