Em um arquivo como este:
...
Pattern2:TheWrongBar
foo
Pattern2:TheRightBar
foo
First Pattern
foo
...
Preciso encontrar a última ocorrência Pattern2
disso antes First Pattern
do que seria neste casoPattern2:TheRightBar
Minha primeira ideia é obter todo o arquivo restante antes First pattern
com:
sed -e '/First Pattern/,$d' myfile | tac | grep -m1 "Pattern I need to get"
Não existe uma maneira de otimizar esse código?
Responder1
Com awk
:
awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file.txt
/Pattern2/ {line=$0; next}
: Se o padrãoPattern2
for correspondido, salvando a linha na variávelline
e indo para a próxima linha/First Pattern/ {print line; exit}
: seFirst Pattern
for encontrado, imprimindo a variávelline
e saindo
Exemplo:
% 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
Responder2
Você poderia correr
sed '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/!d;q' infile
Como funciona:
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)
Isso só sairia se houvesse pelo menos uma correspondência de linha PATTERN2
antes de alguma correspondência de linha PATTERN1
, com uma entrada como
1
2
PATTERN1
PATTERN2--1st
3
PATTERN2--2nd
PATTERN1
...
ele irá imprimir
PATTERN2--2nd
Se você quisesse sair na primeira partida de PATTERN1
qualquer maneira, você executaria
sed -n '/PATTERN2/h;/PATTERN1/!d;x;/PATTERN2/p;q' infile
que não imprime nada com a entrada acima (esta faz exatamente o que sua solução faz).
Responder3
Encontra a contagem de linhas do "Primeiro Padrão" e, em seguida, usa head para exibir as linhas acima dele, passa pelo tac e faz o grep.
head --lines=+"$(grep -nm1 "First Pattern" file | cut -d\: -f1)" file | tac | grep -m1 "Pattern2"
Por exemplo.
head --lines=+6 file | tac | grep -m1 "Pattern2"
Isso é mais confiável do que usar -m 1000000 no grep. Como a velocidade é importante para o OP, verifiquei o tempo de execução e também parece ser mais rápido que todas as outras respostas atuais (no meu sistema)
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
Responder4
Acontece que a maneira mais eficienteNo meu casoera:
grep -m1 "First Pattern" my_file -B 10000000 | tac | grep -m1 "Pattern2"
Obviamente, a -B
opção não pode ser usada em alguns exemplos, mas grep
é muito mais rápida do awk
que sed
optei por essa solução. Se o valor da opção -B
for maior, a busca será bem menos eficiente.