2010-09-15 16 views
155

Duplicar posibles:
How can I search for a multiline pattern in a file ? Use pcregrepexpresiones regulares (GREP) para la búsqueda de varias líneas necesitaba

estoy corriendo un grep para encontrar cualquier archivo sql * que tiene la palabra select seguido por la palabra customerName seguido de la palabra from. Esta instrucción select puede abarcar muchas líneas y puede contener pestañas y nuevas líneas.

He probado algunas variaciones en lo siguiente:

$ grep -liIr --include="*.sql" --exclude-dir="\.svn*" --regexp="select[a-zA-Z0- 
9+\n\r]*customerName[a-zA-Z0-9+\n\r]*from" 

Esto, sin embargo, sólo se ejecuta siempre. ¿Alguien puede ayudarme con la sintaxis correcta, por favor?

+3

El grep que ha indicado aquí se ejecuta para siempre porque no ha especificado ningún archivo para buscar al final del comando ... El '--include' es un filtro de los archivos nombrados y en realidad no proporcionarle los archivos para filtrar. – marklark

Respuesta

359

Sin la necesidad de instalar la variante grep pcregrep, puede hacer la búsqueda de líneas múltiples con grep.

$ grep -Pzo "(?s)^(\s*)\N*main.*?{.*?^\1}" *.c 

Explicación:

-P activar perl-regexp para grep (una poderosa extensión de las extensiones regulares)

-z nueva línea de supresión al final de la línea, por subtituting carácter nulo. Es decir, grep sabe dónde está el final de la línea, pero ve la entrada como una gran línea.

-o print only matching. Debido a que estamos usando -z, el archivo completo es como una sola línea grande, por lo que si hay una coincidencia, se imprimirá el archivo completo; de esta manera no hará eso.

En expresión regular:

(?s) activar PCRE_DOTALL, lo que significa que . encuentra cualquier carácter o de nueva línea

\N encontrar nada excepto una línea nueva, incluso con PCRE_DOTALL activado

.*? encuentran . en modo no codicioso, que es, se detiene tan pronto como sea posible.

^ hallazgo inicial de la línea

\1 referencia inversa al primer grupo (\s*) Esta es una oportunidad para encontrar misma sangría del método

Como se puede imaginar, esta búsqueda imprime el método principal en una C (*.c) archivo fuente.

+13

/bin/grep: las opciones -P y -z no se pueden combinar – Oli

+6

/bin/grep: PCRE no es compatible con \ L, \ l, \ N, \ U, o \ u – Oli

+4

Estoy usando ** GNU grep 2.6.3 **, incluido en ** Ubuntu 11.04 ** y lo hace, ¿cuál es su versión @Oli? – albfan

6

Su problema fundamental es que grep funciona una línea a la vez, por lo que no puede encontrar una instrucción SELECT distribuida entre líneas.

Su segundo problema es que la expresión regular que está utilizando no se ocupa de la complejidad de lo que puede aparecer entre SELECT y FROM, en particular, omite comas, puntos (espacios) y espacios en blanco, pero también comillas y cualquier cosa eso puede estar dentro de una cadena citada.

Probablemente vaya con una solución basada en Perl, haciendo que Perl lea 'párrafos' a la vez y aplicando una expresión regular a eso. La desventaja es tener que lidiar con la búsqueda recursiva; hay módulos para hacer eso, por supuesto, incluido el módulo central File::Find.

En resumen, para un solo archivo:

$/ = "\n\n"; # Paragraphs 

while (<>) 
{ 
    if ($_ =~ m/SELECT.*customerName.*FROM/mi) 
    { 
     printf file name 
     go to next file 
    } 
} 

que necesita ser envuelto en un sub que a continuación se invoca por los métodos de File :: Find.

+0

+1 para las sugerencias y los pensamientos sobre los otros personajes. –

+0

Grep no funciona una línea por vez. Busca en todo el corpus las coincidencias, y solo cuando encuentra una coincidencia vuelve a considerar si una nueva línea está en el medio. De esta forma, no tiene que escanear el corpus en busca de nuevas líneas (lo que ralentizaría significativamente) – Squidly

+0

@MrBones: existe la posibilidad de que una implementación moderna de 'grep' haga lo que diga usando' mmap() ' para leer el archivo en la memoria, pero su modo de operación está definido por la especificación POSIX para ['grep'] (http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html) y definitivamente funciona en términos de líneas Aunque no estoy convencido; si el archivo tiene varios gigabytes, no es necesario mapearlo en la memoria cuando simplemente puede leer algunos kilobytes a la vez (la mayoría de los archivos con líneas tienen menos de kilobytes de longitud). Luego están los archivos JSON, por supuesto, pero son excepcionales. –

137

No soy muy bueno en grep. Pero su problema se puede resolver usando el comando AWK. sólo ver

awk '/select/,/from/' *.sql 

El código anterior resultará de primera aparición de select hasta primera secuencia de from. Ahora necesita verificar si las declaraciones devueltas tienen customername o no. Para esto puedes canalizar el resultado. Y puede usar awk o grep nuevamente.

+0

Me encanta esta solución. Su descripción no está del todo completa - el awk devolverá todas las coincidencias en el (los) archivo (s) - no solo el primero – Paxic

+1

¿Cómo obtener la última ocurrencia en su lugar? – ChocoDeveloper

+2

¿Qué hace la coma aquí, exactamente? – Kev

Cuestiones relacionadas