¿Cómo puedo buscar un patrón multilínea en un archivo?

128

Necesitaba encontrar todos los archivos que contenían un patrón de cadena específico. La primera solución que viene a la mente es usar find piped with xargs grep :

find . -iname '*.py' | xargs grep -e 'YOUR_PATTERN'

Pero si necesito encontrar patrones que abarquen más de una línea, estoy atascado porque grep vainilla no puede encontrar patrones multilínea.

Oli
fuente
2
Este es más viejo, así que diría que no es un duplicado :)
rogerdpack
@rogerdpack Al marcar preguntas como duplicadas, la edad de una pregunta es una preocupación terciaria, después de la cantidad y calidad de las respuestas y la calidad de la pregunta.
tripleee

Respuestas:

98

Entonces descubrí pcregrep que significa Perl Compatible Expresiones regulares GREP .

Por ejemplo, debe buscar archivos donde la variable ' _name ' sea seguida inmediatamente por la variable ' _description ':

find . -iname '*.py' | xargs pcregrep -M '_name.*\n.*_description'

Consejo: debe incluir el carácter de salto de línea en su patrón. Dependiendo de su plataforma, podría ser '\ n', \ r ',' \ r \ n ', ...

Oli
fuente
77
Como menciona Halka a continuación, "también puede persuadir al comodín de puntos para que coincida con las nuevas líneas si agrega (? S) a su expresión regular". Luego use grep con perl regex agregando -P. encontrar . -exec grep -nHP '(? s) SELECT. {1,60} FROM. {1,20} nombre_tabla' '{}' \;
Jim
8
pcregrepestá disponible en Mac conbrew install pcre
Jared Beck
1
Aún mejor: también utilizar -Hque imprime el nombre del archivo antes de cada partido: pcregrep -HM.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
97

¿Por qué no vas por awk ?

awk '/Start pattern/,/End pattern/' filename
Amit
fuente
2
Esto es mucho más fácil de entender y los usos awkque vienen con la mayoría de los sistemas * nix.
Ali Karbassi
24
¡bonito! ¿Hay alguna manera de hacer que este partido no sea codicioso?
marcin
3
¿Cómo imprimirías solo el nombre del archivo cuando hay una coincidencia?
bibstha
2
Puede mostrar los números de línea de las coincidencias con awk '/Start pattern/,/End pattern/ {printf NR " "; print}' filename. Puede que sea más bonita dando a los números de línea un ancho fijo: awk '/Start pattern/,/End pattern/ {printf "%-4s ", NR; print}' filename.
Robert
Esto parece funcionar bien en un solo archivo, sin embargo, ¿qué pasa si me gustaría buscar dentro de varios archivos?
Jinstrong
84

Aquí está el ejemplo usando GNUgrep :

grep -Pzo '_name.*\n.*_description'

-z/ --null-dataTratar los datos de entrada y salida como secuencias de líneas.

Mira también aquí

ayaz
fuente
1
Creo que eso solo representa un único personaje de nueva línea.
Nube
1
No pude usar grep para la búsqueda multilínea, sin usar banderas, -zpor lo que no divide la búsqueda en una sola línea, e -oimprimir solo la parte coincidente.
bbaja42
Descubrí que -o hacía que no imprimiera nada, pero -l trabajé para obtener una lista de archivos (mi comando era grep -rzl pattern *, -rzo no funcionó)
Benubird
55
Recomiendo '' grep -Pazo '' en lugar de '' -Pzo '' para archivos que no sean ASCII. Es mejor porque el modificador -z en archivos que no son ASCII puede desencadenar el comportamiento de "datos binarios" de grep que cambia los valores de retorno. Cambiar '' -a | --text '' evita eso.
Roth
No funciona en Mac con git instalado porbrew reinstall --with-pcre git
Quanlong
21

grep -Ptambién usa libpcre, pero está mucho más instalado. Para encontrar una titlesección completa de un documento html, incluso si abarca varias líneas, puede usar esto:

grep -P '(?s)<title>.*</title>' example.html

Dado que el proyecto PCRE se implementa según el estándar perl, use la documentación perl como referencia:

bukzor
fuente
Hmm intentó esto ahora y no parecía funcionar ... gist.github.com/rdp/0286d91624930bd11d0169d6a6337c33
rogerdpack
No sabía que grep tenía esta opción. Probablemente debido a esto: esto es altamente experimental y grep -P puede advertir sobre características no implementadas. ; eso está bajo CentOS 7. Bajo Fedora 29: Esto es experimental y grep -P puede advertir sobre características no implementadas . Por supuesto, en BSD grep no está allí en absoluto. Sería bueno si no fuera tan experimental, pero es bueno recordarlo, aunque es probable que lo use.
Pryftan
17

Aquí hay un ejemplo más útil:

pcregrep -Mi "<title>(.*\n){0,5}</title>" afile.html

Busca la etiqueta del título en un archivo html incluso si abarca hasta 5 líneas.

Aquí hay un ejemplo de líneas ilimitadas:

pcregrep -Mi "(?s)<title>.*</title>" example.html 
Oli
fuente
44
gracias por esto. Estaba atrapado sin darme cuenta de que un comodín no coincidiría con el personaje de nueva línea.
mate
77
@matt: también puede persuadir al comodín de puntos para que coincida con las nuevas líneas si agrega (?s)a su expresión regular, así:"(?s)<html>.*</html>"
lubomir.brindza
@matt Por supuesto, puede verificar $(al final de un patrón) para indicar que es el final de la línea, aunque eso no es lo mismo que ayudarlo a encontrar múltiples patrones de línea. Ver también glob(7). También puede encontrar este sitio de interés: regular-expressions.info
Pryftan
8

Con buscador de plata :

ag 'abc.*(\n|.)*efg'

Las optimizaciones de velocidad del buscador de plata posiblemente podrían brillar aquí.

Shwaydogg
fuente
4

Se puede utilizar la alternativa grep tamizar aquí (exención de responsabilidad: yo soy el autor).

Admite la coincidencia multilínea y limita la búsqueda a tipos de archivo específicos listos para usar:

sift -m --files '* .py' 'YOUR_PATTERN'

(busque en todos los archivos * .py el patrón de expresión regular multilínea especificado)

Está disponible para todos los principales sistemas operativos. Eche un vistazo a la página de muestras para ver cómo se puede utilizar para extraer valores de varias líneas de un archivo XML.

svent
fuente
3

Esta respuesta puede ser útil:

Regex (grep) para búsqueda multilínea necesaria

Para buscar de forma recursiva, puede usar los indicadores -R (recursivo) e --incluir (patrón GLOB). Ver:

Utilice grep --exclude / - incluya la sintaxis para no grep a través de ciertos archivos

albfan
fuente
@ Ɖiamond ǤeezeƦ tenga en cuenta que la edición de una publicación en el LQP ( stackoverflow.com/review/low-quality-posts/19341146 ) invalida la revisión, así que solo edite si está seguro de que la publicación debe mantenerse.
Fedorqui 'SO deja de dañar'
2

@Marcin: awk ejemplo no codicioso:

awk '{if ($0 ~ /Start pattern/) {triggered=1;}if (triggered) {print; if ($0 ~ /End pattern/) { exit;}}}' filename
Martín
fuente
2
perl -ne 'print if (/begin pattern/../end pattern/)' filename
pbal
fuente
Sin embargo
Herbert
1

Usando ex/ vieditor y la opción globstar (sintaxis similar a awky sed):

ex +"/string1/,/string3/p" -R -scq! file.txt

donde aaaes su punto de partida y bbbes su texto final.

Para buscar de forma recursiva, intente:

ex +"/aaa/,/bbb/p" -scq! **/*.py

Nota: Para habilitar la **sintaxis, ejecute shopt -s globstar(Bash 4 o zsh).

kenorb
fuente