Você não pode transformar o filtro grep a | grep c | grep -v d
em um único% simples grep
. Existem apenas formas complicadas e ineficazes. O resultado tem desempenho lento e o significado da expressão é obscurecido.
Combinação de comando único dos três greps
Se você quiser apenas executar um único comando, poderá usar awk
, que também funciona com expressões regulares e pode combiná-las com operadores lógicos. Aqui está o equivalente do seu filtro:
awk '/a/ && /c/ && $0 !~ /d/'
Eu acho que na maioria dos casos não há razão para simplificar um pipe para um único comando, exceto quando a combinação resulta em uma expressão grep real e simples que pode ser mais rápida (veja os resultados abaixo).
Os sistemas Unix-like são projetados para usar pipes e conectar vários utilitários juntos. Embora a comunicação do tubo não seja a mais eficaz possível, mas na maioria dos casos é suficiente. Porque hoje em dia a maioria dos novos computadores tem múltiplos núcleos de CPU, você pode "naturalmente" utilizar a paralelização de CPU apenas usando um pipe!
Seu filtro original funciona muito bem e acho que, em muitos casos, a solução awk
seria um pouco mais lenta, mesmo em um único núcleo.
Comparação de desempenho
Usando um programa simples , gerou um arquivo de teste aleatório com 200 000 000 linhas, cada uma com 4 caracteres como uma combinação aleatória de caracteres a
, b
, c
e d
. O arquivo tem 1 GB. Durante os testes, ele foi completamente carregado no cache, de modo que nenhuma operação de disco afetou a medição de desempenho. Os testes foram executados no dual core da Intel.
Single grep
$ time ( grep -E '^[^d]*a[^d]*c[^d]*$|^[^d]*c[^d]*a[^d]*$' testfile >/dev/null )
real 3m2.752s
user 3m2.411s
sys 0m0.252s
Único awk
$ time ( awk '/a/ && /c/ && $0 !~ /d/' testfile >/dev/null )
real 0m54.088s
user 0m53.755s
sys 0m0.304s
Os três greps originais canalizados
$ time ( grep a testfile | grep c | grep -v d >/dev/null )
real 0m28.794s
user 0m52.715s
sys 0m1.072s
Híbrido - greps positivos combinados, canalizados negativos
$ time ( grep -E 'a.*c|c.*a' testfile | grep -v d >/dev/null )
real 0m15.838s
user 0m24.998s
sys 0m0.676s
Aqui você vê que o único grep
é muito lento devido à expressão complexa. O pipe original de três greps é bastante rápido por causa de uma boa paralelização. Sem paralelização - em um único núcleo - o canal original é executado apenas um pouco mais rápido que awk
, o que como um processo único não é paralelizado. Awk e grep provavelmente usam o mesmo código de expressões regulares e a lógica das duas soluções é semelhante.
O vencedor claro é o hybring combinando dois greps positivos e deixando o negativo no pipe. Parece que a expressão regular com |
não tem penalidade de desempenho.