Me gustaría saber si hay una manera de combinar una serie de declaraciones grep donde el efecto es "y" las expresiones en lugar de "o" las expresiones coincidentes.
Demostración a continuación:
./script
From one grep statement, I want output like this
a b c
not like this
a
c
a b
a b c
a b c d
Hear es un vistazo al guión.
#!/bin/bash
string="a
b
c
d
a b
a b c
a b c d"
echo -e "\t From one grep statement I want output like this"
echo "$string" |
grep a |grep c |grep -v d #Correct output but pipes three grep statements
echo -e "\n\tNot like this"
echo "$string" |
grep -e'a' -e'c' -e-v'd' #One grep statement but matching expressions are "or" versus "and"
Respuesta aceptada:
No puede transformar el filtro grep a | grep c | grep -v d
a un solo grep
simple . Solo hay formas complicadas e ineficaces. El resultado tiene un rendimiento lento y el significado de la expresión se oscurece.
Combinación de comando único de los tres greps
Si solo desea ejecutar un solo comando, puede usar awk
que también funciona con expresiones regulares y puede combinarlas con operadores lógicos. Este es el equivalente de su filtro:
awk '/a/ && /c/ && $0 !~ /d/'
Creo que, en la mayoría de los casos, no hay razón para simplificar una canalización en un solo comando, excepto cuando la combinación da como resultado una expresión grep relativamente simple que podría ser más rápida (consulte los resultados a continuación).
Los sistemas similares a Unix están diseñados para usar tuberías y conectar varias utilidades entre sí. Aunque la comunicación por tubería no es lo más efectiva posible, en la mayoría de los casos es suficiente. ¡Debido a que hoy en día la mayoría de las computadoras nuevas tienen múltiples núcleos de CPU, puede utilizar "naturalmente" la paralelización de la CPU simplemente usando una tubería!
Tu filtro original funciona muy bien y creo que en muchos casos el awk
la solución sería un poco más lenta incluso en un solo núcleo.
Comparación de rendimiento
Usando un programa simple, generé un archivo de prueba aleatorio con 200 000 000 líneas, cada una con 4 caracteres como una combinación aleatoria de los caracteres a
, b
, c
y d
. El archivo tiene 1 GB. Durante las pruebas, se cargó por completo en el caché, por lo que ninguna operación del disco afectó la medición del rendimiento. Las pruebas se realizaron en Intel de doble núcleo.
Grep único
$ 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
Awk único
$ time ( awk '/a/ && /c/ && $0 !~ /d/' testfile >/dev/null )
real 0m54.088s
user 0m53.755s
sys 0m0.304s
Los tres greps originales 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, canalización negativa
$ time ( grep -E 'a.*c|c.*a' testfile | grep -v d >/dev/null )
real 0m15.838s
user 0m24.998s
sys 0m0.676s
Aquí ves que el único grep
es muy lento debido a la expresión compleja. La tubería original de tres greps es bastante rápida debido a una buena paralelización. Sin paralelización, en un solo núcleo, la canalización original se ejecuta un poco más rápido que awk
que como un solo proceso no está paralelizado. Awk y grep probablemente usan el mismo código de expresiones regulares y la lógica de las dos soluciones es similar.
El claro ganador es el híbrido que combina dos greps positivos y deja el negativo en la tubería. Parece que la expresión regular con |
no tiene penalización de rendimiento.