¿Cómo puedo grep un directorio basado en el contenido de dos líneas sucesivas?

11

¿Cómo puedo seleccionar un directorio para las líneas que contienen "Foo", pero solo obtienen coincidencias cuando la siguiente línea también contiene "Bar"?

Nathan Long
fuente
El problema ahora es totalmente diferente al original: / ¿Quizás sea mejor revertir las versiones anteriores y PUBLICAR otra? Además, la nueva pregunta no está clara para mí.
Gilles Quenot
@sputnick: ¿cómo es eso? Especifiqué un directorio cuando publiqué la pregunta por primera vez; Solo lo en negrita porque la gente no lo notaba.
Nathan Long
No importa, eso funcionará, editaré mi POST en consecuencia.
Gilles Quenot

Respuestas:

7

@ warl0ck me señaló en la dirección correcta pcregrep, pero dije "contiene", no "es", y pregunté sobre un directorio, no un archivo.

Esto parece funcionar para mí.

pcregrep -rMi 'Foo(.*)\n(.*)Bar' .
Nathan Long
fuente
6

Grep en sí no parece ser compatible, use pcregrep:

Foo
Bar
Foo
abc

pcregrep -M "Foo\nBar" file

Tiene:

Foo
Bar
margarita
fuente
3
El OP no dijo eso Fooy Barcomprendería toda la línea.
tojrobinson
6

Con un sedguión:

#!/bin/sed -nf

/^Foo/{
    h         # put the matching line in the hold buffer
    n         # going to nextline
    /^Bar/{   # matching pattern in newline
        H     # add the line to the hold buffer
        x     # return the entire paragraph into the pattern space
        p     # print the pattern space
        q     # quit the script now
    }
}

Para usarlo:

chmod +x script.sed
printf '%s\n' * | ./script.sed

El printfaquí muestran todos los archivos en el directorio actual en una línea cada uno, y pasarlo a sed.

Nota : esto se ordena por orden alfabético.

Más información de útiles pattern spacey hold space AQUÍ .

grymoire.com tiene cosas realmente buenas sobre shellprogramación.

Gilles Quenot
fuente
¿Qué h, n, H, x, p, qsignifica? Muy interesante.
Yamaneko
Ver mis comentarios Más información en pattern space& hold space: grymoire.com/Unix/Sed.html#uh-56 o en francés commentcamarche.net/faq/9536-sed-introduction-a-sed-part-i
Gilles Quenot
POST adaptado para trabajar en un directorio
Gilles Quenot
4

Usando grepsolo, puede construir la siguiente tubería:

grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'

El primero grepobtendrá todas las líneas que contienen Foo, así como la línea después del partido. Luego obtenemos líneas que contienen Bar, así como la línea antes de la coincidencia, y finalmente extraemos las líneas de esta salida que contienen Foo.

EDITAR: Como señaló Manatwork , hay algunos casos problemáticos a tener en cuenta. Aunque es un desafío interesante, debido a grepla funcionalidad orientada a la línea, cualquier solución con él es probable que sea un 'hack' y probablemente sea mejor usar algo como lo pcregrepque es más adecuado para la tarea en cuestión.

tojrobinson
fuente
Agradable. Aunque pregunté por un directorio; Esto parece funcionar:find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
Nathan Long
Eso también enumerará las ocurrencias con "Foo" y "Bar" en la misma línea.
manatwork
@manatwork: Las líneas que contienen "Foo" y "Bar" son "líneas que contienen 'Foo'", que fue lo que se preguntó.
tojrobinson
1
@tojrobinson, ¿qué pasa con el "pero solo obtener coincidencias cuando la siguiente línea también contiene la parte" Bar ""? pastebin.com/Yj8aeCEA
manatwork
3

Si bien prefiero usar la solución de Nathan pcregrep, aquí hay una solución que usa solo grep

grep -o -z -P  'Foo(.*)\n(.*)Bar' file

Explicación de opciones:

  • -oimprimir solo parte coincidente. Necesario ya que la inclusión de -zimprimirá todo el archivo (a menos que haya un \ 0 en alguna parte)
  • -z Trate la entrada como un conjunto de líneas, cada una terminada por un byte cero (el carácter ASCII NUL) en lugar de una nueva línea.
  • -P sintaxis perl regex

EDITAR: esta versión imprime líneas completas coincidentes

    grep -o -P -z  '(.*)Foo(.*)\n(.*)Bar(.*)' file
bbaja42
fuente
1
Genial truco qué -z. Algunos "(. *)" Antes y después de toda la expresión harán que muestre todas las líneas coincidentes. Por ahora, las subcadenas antes de "Foo" y después de "Bar" no se muestran.
manatwork
1

Con awk:

awk '/bar/ && prev != "" {print FILENAME ": " prev "\n" FILENAME ": " $0}
     /foo/ {prev=$0; next}
     {prev=""}' file1...

(nota general sobre la limitación de awk: tenga en cuenta que si algunos nombres de archivo pueden contener caracteres "=", deberá pasarlos como en ./filenamelugar de filenameawk)

Stéphane Chazelas
fuente