¿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.



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 ', ...


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

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í


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:


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 

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


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.


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


@ Ɖ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

2
perl -ne 'print if (/begin pattern/../end pattern/)' filename

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).

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.