Otimizando o grep do GNU

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. Isso implica que eu não posso acumular e passar TODA a entrada para grep de uma só vez (como *.log).

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

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

Vou tentar explicar um pouco mais. Preciso localizar um número fixo de strings com expressões regulares (isso não faz 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 posso usar greptodas as entradas, pois é um fluxo. Posso acumular um pedaço de fluxo e usá grep-lo (portanto não o uso grepem cada linha), mas isso também é limitado (digamos, por 30 segundos).

Eu sei que grepestá criando um NFA a partir de todos os seus padrões (no meu caso, a partir de um arquivo). Então, minha pergunta aqui é: posso dizer greppara salvar esse NFA para a próxima execução, pois não vai mudar? Isso me pouparia o tempo de construir essa NFA todas as vezes.

Bergerg
fonte
O que você quer dizer com Isso é feito em um loop infinito em um fluxo de texto ? Você está dizendo que está executando um greppor linha de texto? De onde vem o texto? Seria tail -fuma opção?
Stéphane Chazelas
Digamos que estou acumulando o fluxo por 30 segundos e depois corro grepnesse pedaço.
bergerg
1
Ainda não está claro por que você precisaria executar grepvárias vezes. Possivelmente relacionado: por que a correspondência de 1250 strings e 90k de padrões é tão lenta?
Stéphane Chazelas
5
grepse destina a trabalhar em um fluxo de texto, ainda não entendi por que você precisaria executar várias instâncias. Por que você não pode alimentar tudo isso na mesma grepinstância? Por que você precisa acumulá- los antes de alimentar grep?
Stéphane Chazelas
2
Dê uma olhada no flex e escreva seu próprio programa, que pode se tornar muito mais rápido.
user2064000

Respostas:

14

Não, não existe. Geralmente, o custo de inicialização grep(bifurcar um novo processo, carregar a biblioteca executável, 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 e 90k de padrões é tão lenta? sobre um bug em algumas versões do GNU grepque o tornaria particularmente lento para um grande número de regexps.

Possivelmente aqui, você pode evitar executar grepvárias vezes alimentando seus pedaços na mesma grepinstância, por exemplo, usando-o como um co-processo e usando um marcador para detectar o fim. Com zshe GNU grepe awkimplementaçõ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 possa ser mais simples fazer tudo com awkou perlnão.

Mas se você não precisa que a grepsaída entre em arquivos diferentes para diferentes pedaços, você sempre pode:

{
  cat chunk1
  while wget -qO- ...; done # or whatever you use to fetch those chunks
  ...
} | grep -Ef patterns > output
Stéphane Chazelas
fonte
Tenho veraion 3+ de grep, então esse não é o problema. Nem sequer considerou a sobrecarga do garfo. Acho que vou tentar transmitir tudo grepcomo está. Obrigado.
bergerg
O executável e as bibliotecas compartilhadas não permaneceriam nos buffers de RAM após o término dos processos (a menos que o OP esteja com pouca memória RAM)?
Dmitry Grigoryev
2
@DmitryGrigoryev, sim, provavelmente ainda precisa ser mapeado no espaço de endereço do processo e fazer a edição do link. É mais como carregar e analisar os dados da localidade, analisar as opções, o ambiente ... O ponto é que o custo do regcomp () é diluído em toda essa sobrecarga. A primeira coisa a fazer ao otimizar seria evitar executar vários greps em primeiro lugar.
Stéphane Chazelas
1

Não posso usar grep em todas as entradas, pois é um fluxo. Eu posso acumular um pedaço de stream e usar grep nele ...

Você sabe que os dutos bloqueiam? Se você canalizar algo para grep e toda a entrada não estiver disponível, o grep aguardará até que esteja disponível e 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, cmd1 | cmd2é que ambos os programas serão iniciados ao mesmo tempo, com um "buffer de bloco" de 65.536 bytes entre eles. Quando cmd2tenta ler e esse buffer está vazio, ele espera um pedaço estar disponível. Quando cmd1tenta gravar e esse buffer estiver cheio, ele aguardará até que seja cmd2lido.

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

EDIT2: greptambém deve imprimir os resultados assim que os encontrar no fluxo. Não há necessidade de o fluxo terminar antes que você possa obter seus resultados.

JoL
fonte
0

Talvez você possa "usar grep em todas as entradas"? Usando nc(netcat), ou através de script, ou através de outras ferramentas similares? Especialmente se o seu arquivo de padrão for de tamanho gerenciável (digamos, menos de 1000 regexps).

Primeiro exemplo : você pode fazer egrepuma conexão de streaming: (aqui exemplo mostrado com nc, mas outros podem ser aplicáveis)

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

(note: você pode até: touch /some/path/results.gzantes de iniciar o nccomando, e ter tail -fnesse arquivo (vazio) para não perder nada. Enfim, o results.gz conterá tudo o que você deseja capturar)

segundo exemplo : você pode até mesmo egrepem uma sessão de shell em execução no momento (e mostrando outra maneira de acompanhar 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 (consulte algumas informações interessantes em: https://swtch.com/~rsc/regexp/regexp1.html )

Olivier Dulac
fonte
Você poderia até usar exemple1 em coisas como uma saída dd, etc.
Olivier Dulac
nota lateral interessante: grep é mais eficiente, quanto maior a parte conhecida do regexp (por exemplo: procurar a string ou regexp sé muito, mais lento que o casamento somethinge isso é muito mais lento que o casamento something even much longer(este último permite que o casamento do regexp pule maior em arquivos grandes, basicamente "divide" o tempo para analisá-lo pela proporção de comprimento (por exemplo, grepping 1 caractere conhecido é quase 40 vezes mais lento do que corresponder a uma sequência de 40 caracteres conhecidos. t prof isso, mas é realmente perceptível).
Olivier Dulac