Obtém a última ocorrência de um padrão antes de outro padrão

2

Em um arquivo como este:

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

Eu preciso encontrar a última ocorrência de Pattern2 antes de First Pattern , o que seria, nesse caso, Pattern2:TheRightBar

Minha primeira ideia é pegar todo o arquivo restante antes de First pattern com:

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

Não há como otimizar este código?

    
por aze 29.09.2016 / 15:56

4 respostas

0

Encontra a contagem de linha de "Primeiro Padrão", então usa a cabeça para exibir as linhas acima dela, canaliza através do tac e passa para ela.

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 do 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
    
por 04.10.2016 / 16:44
2

com awk :

awk '/Pattern2/ {line=$0; next}; /First Pattern/ {print line; exit}' file.txt
  • /Pattern2/ {line=$0; next} : Se o padrão Pattern2 for correspondido, salvando a linha na variável line e indo para a próxima linha

  • /First Pattern/ {print line; exit} : se First Pattern for encontrado, imprimindo a variável line 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
    
por 29.09.2016 / 16:05
1

Você pode 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ó sairá se houver pelo menos uma linha correspondente a PATTERN2 antes de uma linha correspondente a PATTERN1 , com uma entrada como

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

imprimirá

PATTERN2--2nd

Se, em vez disso, você quiser sair na primeira correspondência de PATTERN1 , será executado

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

que não imprime nada com a entrada acima (este faz exatamente o que sua solução faz).

    
por 29.09.2016 / 16:10
0

Acontece que a maneira mais eficiente no meu caso era:

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

Obviamente, a opção -B não pode ser usada em alguns exemplos, mas o grep é muito mais rápido do que awk ou sed que fui com essa solução. Se o valor da opção -B for maior, a pesquisa será bem menos eficiente.

    
por 04.10.2016 / 15:42