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

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

Con grep, quiero seleccionar todas las líneas que coincidan con un patrón y que no coincidan con otro patrón. Quiero poder usar una única 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 que coincidan needle, ignorando las líneas que coincidan ignore me, con una línea del siguiente contexto.

Aquí está mi archivo de entrada:

one needle ignore me
two
three
four needle
five

El resultado 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

Respuesta1

Si tienes GNU grep, puedes usarExpresiones regulares en Perl, que tienen unconstructo de negación.

grep -A1 -P '^(?!.*ignore me).*needle'

Si no tienes GNU grep, puedesemular sus opciones de contexto antes/después en awk.

awk -v after=3 -v before=2 '
/needle/ && !/ignore me/ {
    for (i in h) {
        print h[i];
        delete h[i];
    }
    until = NR + after;
}
{
    if (NR <= until) print $0; else h[NR] = $0;
    delete h[NR-before];
}
END {exit !until}
'

Respuesta2

Parece que estás usando GNU.. Con GNU grep, puede pasar la --perl-regexbandera para activar PCRE y luego proporcionar una aserción negativa de anticipación, como se muestra a continuación

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

Lo principal a tener en cuenta aquí es que (?:(?!STRING).)*es STRINGcomo [^CHAR]*esCHAR

Respuesta3

Sugeriría usar awk en su lugar, ya que maneja mejor la E/S multilínea. Cualquiera1)Canalice los resultados a GNU awk --\ncomo separador de registros, o2)Haz todas las coincidencias en awk.

Opción 1

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

Producción:

four needle                                                                                  
five
--

Tenga en cuenta que esta opción busca en todo el registro ignore me, establece FS=1y compara $1para 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

información relacionada