Como posso corresponder esse texto no awk, imprimindo números de linha para correspondências?

1

Eu tenho um arquivo de texto milhares (aproximadamente 148.000 linhas de comprimento) que consiste em muitas sequências como esta:

b 29.
b 52.
c 84.
c 83.
c 94.
c 93.
c 61.
b 38.
c 81.
c 92.
c 28.
c 37.
c 27.

... e como o arquivo é muito grande, quero poder procurar por padrões como esse (não-funcional one-liner):

grep "b \ 34. \ nc53. \ nb \ 54. \ na \ 45 \ n \ n \ 44 \ n \ 63 \ n \ 64 \ n" nome do arquivo

Parece que o awk é uma boa escolha.

Como posso fazer isso e imprimir números de linha para as correspondências também?

    
por ixtmixilix 11.12.2011 / 23:36

2 respostas

1

Aqui está um script awk que pesquisa uma string multilinha (as correspondências devem consistir em linhas inteiras). Ele recebe o texto para procurar na variável needle . O script funciona criando uma janela de w lines (onde w é o número de linhas em needle ) e comparando com needle .

awk -v needle='b 38.\nc 81.\nc 92.\n' '
    BEGIN {
        if (substr(needle, length(needle)) == "\n")
            needle = substr(needle, 1, length(needle)-1);
        w = split(needle, needles, "\n");
        getline window
        for (i = 2; i < w; i++) {getline; window = window "\n" $0}
    }
    { window = window "\n" $0 }
    window == needle {print NR - w + 1}
    { window = substr(window, index(window, "\n") + 1) }
' <data.txt

Esta não é a maneira mais eficiente de procurar por uma substring, porque cada linha no arquivo de dados é comparada com cada linha no padrão. Existem algoritmos mais eficientes que conseguem realizar menos comparações fazendo algumas pré-computações no padrão, como Knuth-Morris-Pratt .

Para um arquivo que cabe confortavelmente na memória, eu leria tudo de uma vez e executaria a pesquisa na memória. Se tudo o que você está procurando é uma correspondência de padrões, isso é feito facilmente em Perl, mas o Perl não tem primitivas para controlar eficientemente as linhas. Aqui está um script Python que procura por uma string multilinha (que deve ser passada como tal).

import re, sys
needle = sys.argv[1]
haystack = sys.stdin.read()
pos = 0
line = 1
for m in re.finditer(needle, haystack):
    line += haystack.count("\n", pos, m.start())
    pos = m.start()
    print line

Uso: python -c '…' $'b 38.\nc 81.\nc 92.\n' <data.txt

    
por 12.12.2011 / 02:02
1

Eu escreveria assim:

awk -v seven_lines="b 34.c 53.b 54.a 45.d 44.d 63.d 64." '
    seven_lines == l6 l5 l4 l3 l2 l1 $0 {print "pattern found at line " (NR-6)}
    {l6=l5; l5=l4; l4=l3; l3=l2; l2=l1; l1=$0}
'
    
por 12.12.2011 / 01:55