¿Cómo busco múltiples patrones con un patrón que tenga un carácter de tubería?

623

Quiero encontrar todas las líneas en varios archivos que coincidan con uno de los dos patrones. Traté de encontrar los patrones que estoy buscando escribiendo

grep (foo|bar) *.txt

pero el shell lo interpreta |como una tubería y se queja cuando barno es un ejecutable.

¿Cómo puedo buscar múltiples patrones en el mismo conjunto de archivos?

Dan
fuente
posible duplicado de Grep: ¿cómo agregar una condición "OR"?
phuclv
grep 'word1 \ | word2 \ | word3' / ruta / a / archivo
lambodar

Respuestas:

860

Primero, debe proteger el patrón de la expansión del shell. La forma más fácil de hacerlo es poner comillas simples alrededor. Las comillas simples evitan la expansión de cualquier cosa entre ellas (incluidas las barras invertidas); lo único que no puedes hacer es tener comillas simples en el patrón.

grep 'foo*' *.txt

Si necesita una comilla simple, puede escribirla como '\''(literal de cadena final, comilla literal, literal de cadena abierta).

grep 'foo*'\''bar' *.txt

En segundo lugar, grep admite dos sintaxis para patrones. La antigua sintaxis predeterminada ( expresiones regulares básicas ) no admite el |operador alternation ( ), aunque algunas versiones lo tienen como extensión, pero escrito con una barra diagonal inversa.

grep 'foo\|bar' *.txt

La forma portátil es usar la sintaxis más nueva, expresiones regulares extendidas . Debe pasar la -Eopción a greppara seleccionarlo. En Linux, también puede escribir en egreplugar de grep -E(en otros unices, puede hacer que sea un alias).

grep -E 'foo|bar' *.txt

Otra posibilidad cuando solo está buscando cualquiera de varios patrones (en lugar de crear un patrón complejo usando la disyunción) es pasar múltiples patrones a grep. Puede hacer esto precediendo cada patrón con la -eopción.

grep -e foo -e bar *.txt
Gilles
fuente
18
Como nota al margen: cuando los patrones son fijos, realmente debería acostumbrarse fgrepo grep -F, para patrones pequeños, la diferencia será insignificante, pero a medida que se alargan, los beneficios comienzan a mostrarse ...
TC1
77
@ TC1 fgrep está en desuso según la página de
manual
18
@ TC1 Si grep -Ftiene un beneficio de rendimiento real depende de la implementación de grep: algunos de ellos aplican el mismo algoritmo de todos modos, por lo que eso -Fhace la diferencia solo en el tiempo dedicado a analizar el patrón y no en el tiempo de búsqueda. GNU grep no es más rápido con -F, por ejemplo (también tiene un error que hace grep -Fmás lento en configuraciones locales multibyte, ¡el mismo patrón constante con grepes en realidad significativamente más rápido!). Por otro lado, BusyBox grep se beneficia mucho de -Flos archivos grandes.
Gilles
44
Quizás debería mencionarse que para patrones más complicados donde la alternancia solo debe ser para una parte de la expresión regular, se puede agrupar con "\ (" y "\)" (el escape es para las "expresiones regulares básicas" predeterminadas ) (?)
Peter Mortensen
44
Tenga en cuenta que egrepes anterior grep -E. No es específico de GNU (ciertamente no tiene nada que ver con Linux). En realidad, todavía encontrará sistemas como Solaris donde el valor predeterminado greptodavía no es compatible -E.
Stéphane Chazelas
90
egrep "foo|bar" *.txt

o

grep "foo\|bar" *.txt
grep -E "foo|bar" *.txt

citando selectivamente la página de manual de gnu-grep:

   -E, --extended-regexp
          Interpret PATTERN as an extended regular expression (ERE, see below).  (-E is specified by POSIX.)

Matching Control
   -e PATTERN, --regexp=PATTERN
          Use PATTERN as the pattern.  This can be used to specify multiple search patterns, or to protect  a  pattern
          beginning with a hyphen (-).  (-e is specified by POSIX.)

(...)

   grep understands two different versions of regular expression syntax: basic and extended.”  In  GNU grep,  there
   is  no  difference  in  available  functionality  using  either  syntax.   In  other implementations, basic regular
   expressions are less powerful.  The following description applies to extended regular expressions; differences  for
   basic regular expressions are summarized afterwards.

Al principio no leí más, así que no reconocí las sutiles diferencias:

Basic vs Extended Regular Expressions
   In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead  use  the
   backslashed versions \?, \+, \{, \|, \(, and \).

Siempre usé egrep y parens innecesariamente, porque aprendí de los ejemplos. Ahora aprendí algo nuevo. :)

usuario desconocido
fuente
22

Como dijo TC1, -Fparece ser una opción utilizable:

$> cat text
some text
foo
another text
bar
end of file

$> patterns="foo
bar" 

$> grep -F "${patterns}" text
foo
bar
ДМИТРИЙ МАЛИКОВ
fuente
1
@poige No sabía acerca de la opción $ 'foo \ nbar', no estoy seguro de cómo funciona la expansión aquí, necesito buscar, pero gracias, eso es realmente útil.
haridsv
¡Agradable! Esta opción también parece hacer que se ejecute mucho más rápido (ya que deshabilita la expresión regular).
qwertzguy
15

En primer lugar, debe usar comillas para caracteres especiales. Segundo, aun así, grepno entenderá la alternancia directamente; necesitaría usar egrep, o ( grepsolo con GNU ) grep -E.

egrep 'foo|bar' *.txt

(Los paréntesis son innecesarios a menos que la alternancia sea parte de una expresión regular más grande).

geekosaur
fuente
44
En realidad, grep -Ees más estándar que egrep.
jw013
8

Si no necesita expresiones regulares, es mucho más rápido de usar fgrepo grep -Fcon múltiples parámetros -e, como este:

fgrep -efoo -ebar *.txt

fgrep(alternativamente grep -F) es mucho más rápido que grep normal porque busca cadenas fijas en lugar de expresiones regulares.

Moustafa Elqabbany
fuente
44
Consulte también los comentarios en esta página que mencionan que fgrepestá en desuso.
phk
6

Puede probar el siguiente comando para obtener el resultado:

egrep 'rose.*lotus|lotus.*rose' some_file
Abhishek
fuente
3

Una forma barata y alegre de buscar múltiples patrones:

$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq
DHDHDHD
fuente
Podría beneficiarse de una explicación.
Peter Mortensen
2
La explicación es que la -fopción de grep toma un archivo con múltiples patrones. En lugar de crear un archivo temporal (que puede olvidar eliminar después), simplemente use la sustitución del proceso del shell:grep -f <(echo foo; echo bar) *.txt
Jakob
3

Pipe ( |) es un carácter de shell especial, por lo que debe ser escapado ( \|) o citado según el manual ( man bash):

Las comillas se usan para eliminar el significado especial de ciertos caracteres o palabras al shell. Se puede usar para deshabilitar el tratamiento especial para caracteres especiales, para evitar que las palabras reservadas se reconozcan como tales y para evitar la expansión de parámetros.

El encerrar caracteres entre comillas dobles conserva el valor literal de todos los caracteres dentro de las comillas

Una barra invertida no citada ( \) es el carácter de escape.

Ver: ¿Qué personajes se deben escapar en Bash?

Aquí hay algunos ejemplos (usando herramientas no mencionadas aún):

  • Utilizando ripgrep:

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • Utilizando git grep:

    • git grep --no-index -e foo --or -e bar

      Nota: También admite expresiones booleanas como --and, --ory --not.

Para la operación AND por línea, vea: ¿Cómo ejecutar grep con múltiples patrones AND?

Para la operación AND por archivo, consulte: ¿Cómo verificar que existan todas las cadenas múltiples o expresiones regulares en un archivo?

kenorb
fuente
3

Tuve acceso a registros donde las fechas se formatearon estúpidamente: [30 / Jun / 2013: 08: 00: 45 +0200]

Pero necesitaba mostrarlo como: 30 / Jun / 2013 08:00:45

El problema es que al usar "OR" en mi declaración grep, recibí las dos expresiones de coincidencia en dos líneas separadas.

Aquí está la solución:

grep -in myURL_of_interest  *access.log  | \
grep -Eo '(\b[[:digit:]]{2}/[[:upper:]][[:lower:]]{2}/[[:digit:]]{4}|[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\b)'   \
| paste - - -d" " > MyAccess.log
tsmets
fuente
2

TL; DR: si desea hacer más cosas después de hacer coincidir uno de los múltiples patrones, enciérrelos como en \(pattern1\|pattern2\)

ejemplo: Quiero encontrar todos los lugares donde una variable que contiene el nombre 'fecha' se define como una Cadena o int. (por ejemplo, "int cronDate =" o "String textFormattedDateStamp ="):

cat myfile | grep '\(int\|String\) [a-zA-Z_]*date[a-zA-Z_]* =' 

Con grep -E, no necesita escapar de los paréntesis o la tubería, es decir,grep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='

jeremysprofile
fuente
1

Esto funciona para mi

root@gateway:/home/sshuser# aws ec2 describe-instances --instance-ids i-2db0459d |grep 'STATE\|TAG'

**STATE**   80      stopped

**STATE**REASON     Client.UserInitiatedShutdown    Client.UserInitiatedShutdown: User initiated shutdown

**TAGS**    Name    Magento-Testing root@gateway:/home/sshuser#
Mansur Ali
fuente
1

Hay varias formas de hacer esto.

  1. grep 'foo\|bar' *.txt
  2. egrep 'foo|bar' *.txt
  3. find . -maxdepth 1 -type f -name "*.txt" | xargs grep 'foo\|bar'
  4. find . -maxdepth 1 -type f -name "*.txt" | xargs egrep 'foo|bar'

Las opciones 3ra y 4ta solo aparecerán en los archivos y evitarán que los directorios tengan .txtsus nombres.
Entonces, según su caso de uso, puede usar cualquiera de las opciones mencionadas anteriormente.
¡¡Gracias!!

Bhagyesh Dudhediya
fuente
0

para añadir a la respuesta de @ geekosaur , si tiene múltiples patrones que también contienen pestañas y espacio, use el siguiente comando

grep -E "foo[[:blank:]]|bar[[:blank:]]"

donde [[:blank:]]está la clase de caracteres RE que representa un espacio o un carácter de tabulación

Fuseteam
fuente