Vou usar o mesmo arquivo de teste que o thrig:
$ cat file
a
pat 1
pat 2
b
pat 3
Aqui está uma solução para o awk:
$ awk '/pat/ && last {print last; print} {last=""} /pat/{last=$0}' file
pat 1
pat 2
Como funciona
awk
implicitamente percorre todas as linhas do arquivo. Este programa usa uma variável, last
, que contém a última linha se corresponder a regex pat
. Caso contrário, ele contém a string vazia.
-
/pat/ && last {print last; print}
Se
pat
corresponder a essa linha e a linha anterior,last
, também corresponder, imprima as duas linhas. -
{last=""}
Substitua
last
por uma string vazia -
/pat/ {last=$0}
Se esta linha corresponder a
pat
, definalast
para esta linha. Desta forma, estará disponível quando processarmos a próxima linha.
Alternativa para tratar > 2 correspondências consecutivas como um grupo
Vamos considerar este arquivo de teste estendido:
$ cat file2
a
pat 1
pat 2
b
pat 3
c
pat 4
pat 5
pat 6
d
Diferentemente da solução acima, esse código trata as três linhas de correspondência consecutivas como um grupo a ser impresso:
$ awk '/pat/{f++; if (f==2) print last; if (f>=2) print; last=$0; next} {f=0}' file2
pat 1
pat 2
pat 4
pat 5
pat 6
Este código usa duas variáveis. Como antes, last
é a linha anterior. Além disso, f
conta o número de correspondências consecutivas. Assim, imprimimos linhas de correspondência quando f
é 2 ou maior.
Adicionando recursos semelhantes ao grep
Para emular a saída grep
mostrada na pergunta, esta versão imprime o nome do arquivo e o número da linha antes de cada linha correspondente:
$ awk 'FNR==1{f=0} /pat/{f++; if (f==2) printf "%s:%s:%s\n",FILENAME,FNR-1,last; if (f>=2) printf "%s:%s:%s\n",FILENAME,FNR,$0; last=$0; next} {f=0}' file file2
file:2:pat 1
file:3:pat 2
file2:2:pat 1
file2:3:pat 2
file2:7:pat 4
file2:8:pat 5
file2:9:pat 6
As variáveis FILENAME de Awk fornecem o nome do arquivo e o FNR
do awk fornece o número da linha dentro do arquivo.
No início de cada arquivo, FNR==1
, redefinimos f
para zero. Isso impede que a última linha de um arquivo seja considerada consecutiva com a primeira linha do próximo arquivo.
Para quem gosta do código espalhado por várias linhas, o texto acima é semelhante:
awk '
FNR==1{f=0}
/pat/ {f++
if (f==2) printf "%s:%s:%s\n",FILENAME,FNR-1,last
if (f>=2) printf "%s:%s:%s\n",FILENAME,FNR,$0
last=$0
next
}
{f=0}
' file file2