Com o grep, como posso combinar um padrão e inverter a correspondência com outro padrão?

Com o grep, como posso combinar um padrão e inverter a correspondência com outro padrão?

Com grep, quero selecionar todas as linhas que correspondem a um padrão e que não correspondem a outro padrão. Quero poder usar uma única invocação greppara poder usar a --after-contextopção (or --before-context, or --context).

-vnão é viável aqui, pois nega todos os padrões que passo para grepusar a -eopção.

Exemplo

Quero procurar linhas correspondentes needle, ignorando as linhas correspondentes ignore me, com uma linha do seguinte contexto.

Aqui está meu arquivo de entrada:

one needle ignore me
two
three
four needle
five

A saída que eu quero é:

four needle
five

Como você pode ver, esta solução ingênua não funciona:

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

Responder1

Se você possui GNU grep, você pode usarExpressões regulares Perl, que possuem umconstrução de negação.

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

Se você não possui o GNU grep, você podeemular suas opções de contexto antes/depois no 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}
'

Responder2

Você parece estar usando GNU. Com o GNU grep, você poderia passar o --perl-regexsinalizador para ativar o PCRE e então fornecer uma afirmação antecipada negativa, exemplo abaixo

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

A principal coisa a notar aqui é que (?:(?!STRING).)*é STRINGcomo [^CHAR]*éCHAR

Responder3

Eu sugeriria usar o awk, pois ele lida melhor com E / S multilinha. Qualquer1)Canalize os resultados para GNU awk --\ncomo separador de registros, ou2)Faça todas as correspondências no awk.

Opção 1

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

Saída:

four needle                                                                                  
five
--

Observe que esta opção pesquisa todo o registro ignore me, define FS=1e compara $1para comparar apenas com a primeira linha.

opção 2

<file awk 'a-- > 0; $0 ~ re1 && $0 !~ re2 { print $0; a=after }' re1=needle re2='ignore me' after=1

informação relacionada