De todas as soluções propostas até agora, minha solução original usando o grep é a mais rápida, terminando em 25 segundos. A desvantagem é que é tedioso adicionar e remover palavras-chave. Então eu criei um script (apelidado multi
) que simula o comportamento, mas permite mudar a sintaxe:
#!/bin/bash
# Usage: multi [z]grep PATTERNS -- FILES
command=$1
# first two arguments constitute the first command
command_head="$1 -le '$2'"
shift 2
# arguments before double-dash are keywords to be piped with xargs
while (("$#")) && [ "$1" != -- ] ; do
command_tail+="| xargs $command -le '$1' "
shift
done
shift
# remaining arguments are files
eval "$command_head $@ $command_tail"
Agora, escrever multi grep one two three -- *
é equivalente à minha proposta original e é executado no mesmo tempo. Também posso usá-lo facilmente em arquivos compactados usando zgrep
como o primeiro argumento.
Outras soluções
Eu também experimentei um script Python usando duas estratégias: pesquisar todas as palavras-chave linha por linha e pesquisar em todo o arquivo palavra-chave por palavra-chave. A segunda estratégia foi mais rápida no meu caso. Mas foi mais lento do que usar apenas grep
, terminando em 33 segundos. A correspondência de palavras-chave linha por linha terminou em 60 segundos.
#!/usr/bin/python3
import gzip, sys
i = sys.argv.index('--')
patterns = sys.argv[1:i]
files = sys.argv[i+1:]
for f in files:
with (gzip.open if f.endswith('.gz') else open)(f, 'rt') as s:
txt = s.read()
if all(p in txt for p in patterns):
print(f)
O script dado por terdon terminou em 54 segundos. Na verdade, levou 39 segundos de tempo de parede, porque meu processador é dual core. O que é interessante, porque meu script Python levou 49 segundos de tempo de parede (e grep
foi de 29 segundos).
O script por cas falhou em terminar em tempo razoável, mesmo em um número menor de arquivos processados com grep
em 4 segundos, então tive que matá-lo.
Mas sua proposta original awk
, embora seja mais lenta que grep
, tem vantagem potencial. Em alguns casos, pelo menos na minha experiência, é possível esperar que todas as palavras-chave apareçam em algum lugar na cabeça do arquivo, se elas estiverem no arquivo. Isto dá a esta solução um aumento dramático no desempenho:
for f in *; do
zcat $f | awk -v F=$f \
'NR>100 {exit} /one/ {a++} /two/ {b++} /three/ {c++} a&&b&&c {print F; exit}'
done
Termina em um quarto de segundo, ao contrário de 25 segundos.
Naturalmente, talvez não tenhamos a vantagem de procurar palavras-chave que ocorram perto do início dos arquivos. Nesse caso, a solução sem NR>100 {exit}
leva 63 segundos (50s de tempo de parede).
Arquivos descompactados
Não há diferença significativa no tempo de execução entre minha grep
solution e cas ' awk
proposal, ambos levam uma fração de segundo para serem executados.
Observe que a inicialização da variável FNR == 1 { f1=f2=f3=0; }
é obrigatória nesse caso para redefinir os contadores de cada arquivo processado subsequente. Como tal, esta solução requer a edição do comando em três locais, se você quiser alterar uma palavra-chave ou adicionar novas. Por outro lado, com grep
você pode simplesmente anexar | xargs grep -l four
ou editar a palavra-chave desejada.
Uma desvantagem da solução grep
que usa a substituição de comandos, é que ela irá travar em qualquer lugar da cadeia, antes da última etapa, não há arquivos correspondentes. Isso não afeta a variante xargs
porque o canal será cancelado quando grep
retornar um status diferente de zero. Eu atualizei meu script para usar xargs
, então eu não tenho que lidar com isso sozinho, tornando o script mais simples.