¿Cómo ejecutar grep con múltiples patrones AND?


86

Me gustaría obtener la coincidencia de múltiples patrones con AND implícito entre patrones, es decir, equivalente a ejecutar varios greps en una secuencia:

grep pattern1 | grep pattern2 | ...

Entonces, ¿cómo convertirlo en algo así?

grep pattern1 & pattern2 & pattern3

Me gustaría usar grep único porque estoy construyendo argumentos dinámicamente, por lo que todo tiene que encajar en una cadena. El uso del filtro es una característica del sistema, no grep, por lo que no es un argumento para ello.


No confunda esta pregunta con:

grep "pattern1\|pattern2\|..."

Esta es una coincidencia de múltiples patrones OR .


Similar:
unir

Respuestas:


79

agrep puede hacerlo con esta sintaxis:

agrep 'pattern1;pattern2'

Con GNU grep, cuando se construye con soporte PCRE, puede hacer:

grep -P '^(?=.*pattern1)(?=.*pattern2)'

Con astgrep :

grep -X '.*pattern1.*&.*pattern2.*'

(agregar .*s como <x>&<y>coincide con cadenas que coinciden con ambas <x>y <y> exactamente , a&bnunca coincidiría, ya que no existe una cadena que pueda ser ambas ay bal mismo tiempo).

Si los patrones no se superponen, también puede hacer:

grep -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

La mejor forma portátil es probablemente awkcomo ya se mencionó:

awk '/pattern1/ && /pattern2/'

Con sed:

sed -e '/pattern1/!d' -e '/pattern2/!d'

Tenga en cuenta que todas ellas tendrán una sintaxis de expresión regular diferente.


1
La agrepsintaxis no funciona para mí ... ¿en qué versión se introdujo?
Raman

@Raman 2.04 de 1992 ya lo tenía. No tengo motivos para creer que no estaba allí desde el principio. agrepSe pueden encontrar versiones más nuevas (después de 1992) incluidas con glimpse / webglimpse . Posiblemente tenga una implementación diferente. Sin embargo, tuve un error con la versión ast-grep, la opción para expresiones regulares aumentadas es -X, no -A.
Stéphane Chazelas

@ StéphaneChazelas Gracias, tengo agrep0.8.0 en Fedora 23. Esto parece ser diferente al agrepque usted hace referencia.
Raman

1
@Raman, el tuyo suena como TREagrep .
Stéphane Chazelas

2
@Techiee, o simplementeawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Stéphane Chazelas

19

No especificó la versión grep, esto es importante. Algunos motores regexp permiten múltiples coincidencias agrupadas por AND usando '&', pero esta es una característica no estándar y no portátil. Pero, al menos GNU grep no es compatible con esto.

OTOH simplemente puede reemplazar grep con sed, awk, perl, etc. (enumerados en orden de aumento de peso). Con awk, el comando se vería así

awk '/ regexp1 / && / regexp2 / && / regexp3 / {print; } '

y puede construirse para especificarse en la línea de comandos de manera fácil.


3
Solo recuerde que awkusa ERE, por ejemplo, el equivalente de grep -E, en oposición a los BRE que grepusa.
jw013

3
awkLas expresiones regulares se llaman ERE, pero de hecho son un poco idiosincrásicas. Aquí hay probablemente más detalles de los que a nadie le interesan: wiki.alpinelinux.org/wiki/Regex
dubiousjim

Gracias, grep 2.7.3 (openSUSE). Te voté, pero mantendré la pregunta abierta por un tiempo, tal vez haya algún truco para grep (no es que no me guste awk, simplemente saber que más es mejor).
greenoldman

2
La acción predeterminada es imprimir la línea coincidente para que la { print; }parte no sea realmente necesaria o útil aquí.
tripleee

7

Si patternscontiene un patrón por línea, puede hacer algo como esto:

awk 'NR==FNR{a[$0];next}{for(i in a)if($0!~i)next}1' patterns -

O esto coincide con subcadenas en lugar de expresiones regulares:

awk 'NR==FNR{a[$0];next}{for(i in a)if(!index($0,i))next}1' patterns -

Para imprimir todo en lugar de ninguna línea de la entrada en el caso de que patternsesté vacía, reemplace NR==FNRcon FILENAME==ARGV[1]o con ARGIND==1in gawk.

Estas funciones imprimen las líneas de STDIN que contienen cada cadena especificada como argumento como una subcadena. gasignifica grep all e gaiignora mayúsculas y minúsculas.

ga(){ awk 'FILENAME==ARGV[1]{a[$0];next}{for(i in a)if(!index($0,i))next}1' <(printf %s\\n "$@") -; }
gai(){ awk 'FILENAME==ARGV[1]{a[tolower($0)];next}{for(i in a)if(!index(tolower($0),i))next}1' <(printf %s\\n "$@") -; }

7

Esta no es una muy buena solución, pero ilustra un "truco" algo genial

function chained-grep {
    local pattern="$1"
    if [[ -z "$pattern" ]]; then
        cat
        return
    fi    

    shift
    grep -- "$pattern" | chained-grep "$@"
}

cat something | chained-grep all patterns must match order but matter dont

1
Use cualquiera chained-grep()o function chained-grepno function chained-grep(): unix.stackexchange.com/questions/73750/…
nisetama

3

git grep

Aquí está la sintaxis que git grepcombina varios patrones usando expresiones booleanas :

git grep --no-index -e pattern1 --and -e pattern2 --and -e pattern3

El comando anterior imprimirá líneas que coincidan con todos los patrones a la vez.

--no-index Buscar archivos en el directorio actual que no es administrado por Git.

Busca man git-grepayuda.

Ver también:

Para la operación OR , vea:


1

ripgrep

Aquí está el ejemplo usando rg:

rg -N '(?P<p1>.*pattern1.*)(?P<p2>.*pattern2.*)(?P<p3>.*pattern3.*)' file.txt

Es una de las herramientas de grepping más rápidas, ya que está construida sobre el motor regex de Rust que utiliza autómatas finitos, SIMD y optimizaciones literales agresivas para que la búsqueda sea muy rápida.

Consulte también la solicitud de características relacionadas en GH-875 .


1

Aquí está mi opinión, y esto funciona para palabras en varias líneas:

Use find . -type fseguido de tantos
-exec grep -q 'first_word' {} \;
y la última palabra clave con
-exec grep -l 'nth_word' {} \;

-q
-larchivos de programa silencioso / silencioso con coincidencias

La siguiente lista devuelve nombres de archivos con las palabras 'conejo' y 'agujero' en ellos:
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;


-2

Para encontrar TODAS las palabras (o patrones), puede ejecutar grep en el bucle FOR . La principal ventaja aquí es buscar en una lista de expresiones regulares .

EDITE mi respuesta con un ejemplo real:

# search_all_regex_and_error_if_missing.sh 

find_list="\
^a+$ \
^b+$ \
^h+$ \
^d+$ \
"

for item in $find_list; do
   if grep -E "$item" file_to_search_within.txt 
   then
       echo "$item found in file."
   else
       echo "Error: $item not found in file. Exiting!"
       exit 1
   fi
done

Ahora vamos a ejecutarlo en este archivo:

hhhhhhhhhh

aaaaaaa

bbbbbbbbb

ababbabaabbaaa

ccccccc

dsfsdf

bbbb

cccdd

Automóvil club británico

caa

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ encontrado en el archivo.

bbbbbbbbb bbbb

^ b + $ encontrado en el archivo.

hhhhhhhhhh

^ h + $ encontrado en el archivo.

Error: ^ d + $ no encontrado en el archivo. Saliendo!


1
Su lógica es defectuosa: pregunté por el ALLoperador, su código funciona como ORoperador, no AND. Y por cierto. para eso ( OR) es una solución mucho más fácil dada en la pregunta.
greenoldman

@greenoldman La lógica es simple: for for realizará un bucle en TODAS las palabras / patrones de la lista, y si se encuentra en el archivo, lo imprimirá. Tan solo elimine el else si no necesita acción en caso de que no se encuentre la palabra.
Noam Manos

1
Entiendo su lógica y mi pregunta: estaba preguntando sobre el ANDoperador, lo que significa que el archivo es solo un golpe positivo si coincide con el patrón A y el patrón B y el patrón C y ... ANDEn su caso, el archivo es un golpe positivo si coincide patrón A o patrón B o ... ¿Ves la diferencia ahora?
greenoldman

@greenoldman, ¿no está seguro de por qué cree que este bucle no verifica la condición AND para todos los patrones? Así que he editado mi respuesta con un ejemplo real: buscará en el archivo todas las expresiones regulares de la lista, y en la primera que falta, saldrá con error.
Noam Manos

Lo tienes justo en frente de tus ojos, tienes una coincidencia positiva justo después de que se ejecute la primera coincidencia. Debería haber "recolectado" todos los resultados y calcularlos AND. Luego, debe volver a escribir el script para que se ejecute en varios archivos; entonces tal vez se dé cuenta de que la pregunta ya está respondida y su intento no trae nada a la mesa, lo siento.
greenoldman
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.