Con grep, ¿cómo puedo hacer coincidir un patrón e invertir hacer coincidir otro patrón?

11

Con grep, quiero seleccionar todas las líneas que coinciden con un patrón, y que no coinciden con otro patrón. Quiero poder usar una sola invocación de greppara poder usar la --after-contextopción (o --before-context, o --context).

-vno es viable aquí, ya que niega todos los patrones que paso al grepusar la -eopción.

Ejemplo

Quiero buscar líneas coincidentes needle, ignorando líneas coincidentes ignore me, con una línea de contexto siguiente.

Aquí está mi archivo de entrada:

one needle ignore me
two
three
four needle
five

La salida que quiero es:

four needle
five

Como puede ver, esta solución ingenua no funciona:

$ cat file | grep --after-context=1 needle | grep -v 'ignore me'
two
---
four needle
five
Flimm
fuente

Respuestas:

8

Parece que estás usando GNU . Con GNU grep, puede pasar la --perl-regexbandera para activar PCRE y luego proporcionar una afirmación negativa anticipada, ejemplo a continuación

grep --after-context=1 \
--perl-regex '^(?:(?!ignore me).)*needle(?:(?!ignore me).)*$' file.txt
four needle
five

Lo más importante aquí es que (?:(?!STRING).)*es STRINGcomo [^CHAR]*esCHAR

iruvar
fuente
@ 1_CR ... Señor ... es increíble ...: P algo sonríe aack
Rahul Patil
@RahulPatil. :-), sí GNU grep es tan bueno.
iruvar
Eso no es exactamente lo que quiero. Quiero que funcione si "ignorarme" es antes o después de "aguja".
Flimm
@RahulPatil, gracias, lo arreglé en la última versión
iruvar
Muy útil. Especialmente en el caso de grep con contexto donde desea excluir líneas que coinciden estrechamente pero sin una cierta parte del patrón. Cerca de la pregunta original pero no exactamente igual.
Gaoithe
2

Sugeriría usar awk en su lugar, ya que maneja IO multilínea mejor. Ya sea 1) canalizar los resultados a awk GNU con --\ncomo separador de registro, o 2) hacer todo el juego en awk.

Opción 1

<file grep -A1 needle | awk '!/ignore me/' RS='--\n' ORS='--\n'

Salida:

four needle                                                                                  
five
--

Tenga en cuenta que esta opción busca en todo el registro ignore me, establece FS=1y $1compara para comparar solo con la primera línea.

opcion 2

<file awk 'a-- > 0; $0 ~ re1 && $0 !~ re2 { print $0; a=after }' re1=needle re2='ignore me' after=1
Thor
fuente
¿Hay múltiples ignore meen el archivo entonces, awk no funciona
Rahul Patil
@RahulPatil: ¿podría reformular o agregar más detalles a su pregunta? No entiendo lo que preguntas.
Thor
@Thos prueba tu ejemplo con este archivo de entrada paste.ubuntu.com/6252860
Rahul Patil
@RahulPatil: veo lo que quiere decir ahora, la opción 1 supone que hay un --\ndelimitador entre cada grupo coincidente, que no está allí si los grupos son adyacentes entre sí. La forma en que se deben manejar los grupos adyacentes es específica de la tarea, por lo que esto no es necesariamente incorrecto. La opción 2 no depende del separador y no se ve afectada.
Thor