Otimizando o GNU grep

8

Estou usando o egrep ( grep -E ) com um arquivo PATTERN. ( -f path/to/file ).

Isso é feito em um loop infinito em um fluxo de texto. Isto implica que eu não posso acumular e passar TODA a entrada para grep de uma vez (como *.log ).

Existe uma maneira de fazer o grep "salvar" o NFA que ele está construindo do arquivo PATTERN para usar na próxima execução?

Eu pesquisei no Google e li a documentação sem sorte.

Vou tentar explicar um pouco mais. Eu preciso localizar um número fixo de seqüências de caracteres com expressões regulares (isso não é uma parte de uma pergunta, mas fique à vontade para sugerir o contrário), como endereços IP, domínios etc. A pesquisa é feita em um feed da Internet. Você pode pensar nisso como um fluxo de texto. Não consigo usar grep em todas as entradas, pois é um fluxo. Eu posso acumular um pedaço de fluxo e usar grep (não usando grep em cada linha), mas isso também é limitado (digamos por 30 segundos).

Eu sei que grep está criando um NFA de todos os seus padrões (no meu caso, de um arquivo). Então, minha pergunta aqui é: posso dizer a grep para salvar esse NFA para a próxima execução, já que ele não vai mudar? Isso me pouparia o tempo de construir esse NFA toda vez.

    
por bergerg 11.09.2017 / 12:34

3 respostas

14

Não, não existe tal coisa. Geralmente, o custo de iniciar grep (bifurcar um novo processo, carregar o executável, biblioteca compartilhada, ligação dinâmica ...) seria muito maior do que compilar os regexps, portanto, esse tipo de otimização faria pouco sentido.

Embora veja Por que a correspondência de 1250 strings contra padrões de 90k é tão lenta? sobre um bug em algumas versões do GNU grep que tornaria particularmente lento para um grande número de regexps.

Possivelmente aqui, você poderia evitar executar grep várias vezes alimentando seus fragmentos com a mesma grep instance, por exemplo, usando-o como co-processo e usando um marcador para detectar o fim. Com zsh e% GNU grep e awk implementações diferentes de mawk :

coproc grep -E -f patterns -e '^@@MARKER@@$' --line-buffered
process_chunk() {
  { cat; echo @@MARKER@@; } >&p & awk '$0 == "@@MARKER@@"{exit};1' <&p
}
process_chunk < chunk1 > chunk1.grepped
process_chunk < chunk2 > chunk2.grepped

Embora seja mais simples fazer a coisa toda com awk ou perl .

Mas, se você não precisar que a saída grep entre em arquivos diferentes para partes diferentes, sempre será possível fazer isso:

{
  cat chunk1
  while wget -qO- ...; done # or whatever you use to fetch those chunks
  ...
} | grep -Ef patterns > output
    
por 11.09.2017 / 13:00
1

I can't use grep on all of the input since it's a stream. I can accumulate a chunk of stream and use grep on it...

Você está ciente de que o bloqueio de pipelines? Se você enviar algo para grep e toda a entrada não estiver disponível, o grep irá esperar até que esteja disponível e então continuará como se a entrada estivesse lá o tempo todo.

$ ( echo a1; echo b1; sleep 5; echo a2 ) | grep 'a.'
a1
a2

EDIT: Como os pipelines funcionam, por exemplo, com cmd1 | cmd2 é que ambos os programas começarão ao mesmo tempo, com um, e. 65,536-byte "chunk buffer" entre eles. Quando cmd2 tentar ler e esse buffer estiver vazio, ele aguardará a disponibilidade de um pedaço. Quando cmd1 tentar escrever e esse buffer estiver cheio, ele esperará até que cmd2 o leia.

Pelo que eu posso ler, não há necessidade de cortar a entrada em pedaços e passá-los para o grep separadamente. Isso já é feito automaticamente.

EDIT2: grep também deve imprimir os resultados assim que eles forem encontrados no fluxo. Não há necessidade de o fluxo terminar antes que você possa obter seus resultados.

    
por 11.09.2017 / 21:36
0

Talvez você possa "usar o grep em todas as entradas"? Usando nc (netcat) ou através de script , ou através de outras ferramentas semelhantes? Especialmente se o seu patternfile é de tamanho manejável (digamos menos que 1000 regexps).

Primeiro exemplo : você pode usar egrep de conexão de streaming:  (aqui exemplo mostrado com nc , mas outros podem se aplicar)

prompt:/some/path $ nc somehost someport | egrep -f patternfile | gzip -c - > results.gz

# and while this is running, you can have a look at the growing results.gz:
prompt:/some/otherpath $ tail -f /some/path/results.gz | gzip -c - | less

(nota: você pode até: touch /some/path/results.gz antes de iniciar o comando nc , e ter tail -f nesse arquivo (vazio) para não perder nada. De qualquer forma, o results.gz conterá tudo o que você deseja capturar )

second exemple : você pode até egrep em uma sessão de shell em execução no momento (e mostrar outra maneira de seguir a progressão):

#in 1 terminal:
prompt:/home/userA $ script
Script command is started. The file is typescript.
prompt:/home/userA $ 
 ... doing here whatever you want (start IRC? etc) ...
prompt:/home/userA $ ctrl-d # to end the current script session
Script command is complete. The file is typescript.

#and in another terminal, while you are "doing here whatever you want" :
prompt:/home/somewhere $ tail -f /home/userA/typescript | egrep -f patternfile  | tee /some/place/to/store/results.gz

egrep é uma versão altamente eficiente de grep , na maioria dos sistemas  (veja algumas informações sobre interresting em: link )

    
por 11.09.2017 / 17:59

Tags