Grep buscando dos palabras en línea

46

He estado tratando de encontrar una manera de filtrar una línea que contenga las palabras "limón" y "arroz". Sé cómo encontrar "limón" o "arroz", pero no los dos. No necesitan estar uno al lado del otro, solo uno en la misma línea de texto.

Sebastian
fuente
1
Para encontrar todas las cadenas dentro de un archivo, puede ejecutar grep en el bucle FOR: unix.stackexchange.com/a/462445/43233
Noam Manos

Respuestas:

62

"Ambos en la misma línea" significa "'arroz' seguido de caracteres aleatorios seguidos de 'limón' o al revés".

En regex que es rice.*lemono lemon.*rice. Puedes combinar eso usando |:

grep -E 'rice.*lemon|lemon.*rice' some_file

Si desea utilizar expresiones regulares normales en lugar de expresiones extendidas ( -E), necesita una barra diagonal inversa antes de |:

grep 'rice.*lemon\|lemon.*rice' some_file

Para más palabras que rápidamente se vuelven un poco largas y generalmente es más fácil usar múltiples llamadas de grep, por ejemplo:

grep rice some_file | grep lemon | grep chicken
Florian Diesch
fuente
Su última línea es una conjunción, no una disyunción, ¿no? A saber: las grep ricelíneas de búsqueda que contienen rice. Se alimenta en el grep lemonque solo encontrará líneas que contengan limón ... y así sucesivamente. Mientras que el OP, así como sus respuestas anteriores, permiten cualquiera de [arroz | limón | pollo]
javadba
Versión del script: askubuntu.com/a/879253/5696
Jeff
@Florian Diesch - ¿Te importaría explicar por qué |hay que escapar grep? ¡Gracias!
fugitivo
1
@fugitive egrepusa expresiones regulares extendidas donde |se entiende como lógica OR. grepel valor predeterminado es regex básico, donde \|es OR
Sergiy Kolodyazhnyy
Como se indica en grepla página del manual, egrepestá en desuso y debe reemplazarse por grep -E. Me tomé la libertad de editar la respuesta en consecuencia.
postre
26

Puede canalizar la salida del primer comando grep a otro comando grep y eso coincidiría con ambos patrones. Entonces, puedes hacer algo como:

grep <first_pattern> <file_name> | grep <second_pattern>

o,

cat <file_name> | grep <first_pattern> | grep <second_pattern>

Ejemplo:

Agreguemos algunos contenidos a nuestro archivo:

$ echo "This line contains lemon." > test_grep.txt
$ echo "This line contains rice." >> test_grep.txt
$ echo "This line contains both lemon and rice." >> test_grep.txt
$ echo "This line doesn't contain any of them." >> test_grep.txt
$ echo "This line also contains both rice and lemon." >> test_grep.txt

Qué contiene el archivo:

$ cat test_grep.txt 
This line contains lemon.
This line contains rice.
This line contains both lemon and rice.
This line doesn't contain any of them.
This line also contains both rice and lemon.

Ahora, busquemos lo que queremos:

$ grep rice test_grep.txt | grep lemon
This line contains both lemon and rice.
This line also contains both rice and lemon.

Solo obtenemos las líneas donde ambos patrones coinciden. Puede extender esto y canalizar la salida a otro comando grep para otras coincidencias "Y".

Aditya
fuente
21

Aunque la pregunta pide 'grep', pensé que podría ser útil publicar una solución simple 'awk':

awk '/lemon/ && /rice/'

Esto puede extenderse fácilmente con más palabras u otras expresiones booleanas además de 'y'.

David B.
fuente
11

Otra idea para encontrar las coincidencias en cualquier orden es usar:

grep con la opción -P (Perl-Compatibility) y regex positivo anticipado(?=(regex)) :

grep -P '(?=.*?lemon)(?=.*?rice)' infile

o puede usar a continuación, en su lugar:

grep -P '(?=.*?rice)(?=.*?lemon)' infile
  • Los .*?medios coinciden con los caracteres .que ocurren cero o más veces *mientras son opcionales seguidos de un patrón ( riceo lemon). El ?hace que todo sea opcional antes de que (medios cero o una vez de todo lo emparejados .*)

(?=pattern): Búsqueda hacia adelante positiva: La construcción de búsqueda hacia adelante positiva es un par de paréntesis, con el paréntesis de apertura seguido de un signo de interrogación y un signo igual.

Entonces esto devolverá todas las líneas con contiene ambos lemony riceen orden aleatorio. Además, esto evitará el uso de |sy doblado greps.


Enlaces externos: Temas Grep avanzados Lookahead positivo - GREP para diseñadores

αғsнιη
fuente
5
grep -e foo -e goo

Devolverá partidos para foo o goo

netskink
fuente
1

Si admitimos que proporcionar una respuesta que no está grepbasada es aceptable, como la respuesta anterior basada en awk, propondría una perllínea simple como:

$ perl -ne 'print if /lemon/ and /rice/' my_text_file

La búsqueda puede ignorar mayúsculas y minúsculas con algunas / todas las palabras como /lemon/i and /rice/i. En la mayoría de las máquinas Unix / Linux, perl está instalado y awk de todos modos.

Gilles Maisonneuve
fuente
Rechazado !!! ;) Porque no tiene sentido .. :)
An0n
0

Aquí hay un script para automatizar la solución de tuberías grep:

#!/bin/bash

# Use filename if provided as environment variable, or "foo" as default
filename=${filename-foo}

grepand () {
# disable word splitting and globbing
IFS=
set -f
if [[ -n $1 ]]
then
grep -i "$1" ${filename} | filename="" grepand "${@:2}"
else
# If there are no arguments, assume last command in pipe and print everything
cat
fi
}

grepand "$@"
Jeff
fuente
1
Esto probablemente debería implementarse usando una función recursiva, en lugar de construir una cadena de comando e evaling, que se rompe fácilmente
muru
@muru Siéntase libre de sugerir una edición. Agradezco el comentario.
Jeff
1
Editarlo hará que sea una gran reescritura, por lo que no haré eso. Si desea agregarlo, así es como me imagino que debería verse: paste.ubuntu.com/23915379
muru