Wie kann ich mit grep ein Muster abgleichen und die Übereinstimmung mit einem anderen Muster umkehren?

Wie kann ich mit grep ein Muster abgleichen und die Übereinstimmung mit einem anderen Muster umkehren?

Mit grepmöchte ich alle Zeilen auswählen, die einem Muster entsprechen und nicht einem anderen Muster. Ich möchte einen einzigen Aufruf von verwenden können, grepdamit ich die --after-contextOption (oder --before-context, oder --context) verwenden kann.

-vist hier nicht durchführbar, da es alle Muster negiert, die ich grepmit der -eOption übergebe.

Beispiel

Ich möchte nach übereinstimmenden Zeilen suchen und übereinstimmende Zeilen mit einer Zeile des folgenden Kontexts needleignorieren .ignore me

Hier ist meine Eingabedatei:

one needle ignore me
two
three
four needle
five

Die gewünschte Ausgabe ist:

four needle
five

Wie Sie sehen, funktioniert diese naive Lösung nicht:

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

Antwort1

Wenn Sie GNU grep haben, können Sie verwendenReguläre Ausdrücke in Perl, die eineNegationskonstrukt.

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

Wenn Sie GNU grep nicht haben, können Sieemulieren Sie die Vorher/Nachher-Kontextoptionen in 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}
'

Antwort2

Sie scheinen GNU zu verwenden. Mit GNU grep können Sie das --perl-regexFlag zur Aktivierung von PCRE übergeben und dann eine negative Lookahead-Assertion liefern, Beispiel unten

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

Das Wichtigste, was hier zu beachten ist, ist, (?:(?!STRING).)*wie STRINGes [^CHAR]*istCHAR

Antwort3

Ich würde stattdessen awk empfehlen, da es mehrzeilige IO besser handhabt. Entweder1)Leiten Sie die Ergebnisse an GNU awk weiter, wobei --\nals Datensatztrennzeichen verwendet wird, oder2)Führen Sie alle Abgleiche in awk durch.

Option 1

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

Ausgabe:

four needle                                                                                  
five
--

Beachten Sie, dass diese Option den gesamten Datensatz durchsucht ignore meund nur mit der ersten Zeile vergleicht FS=1.$1

Option 2

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

verwandte Informationen