Wie kann ich mit grep ein Muster abgleichen und ein anderes Muster invertieren?


11

Mit grepmöchte ich alle Linien auswählen, die einem Muster entsprechen und die keinem anderen Muster entsprechen. Ich möchte in der Lage sein, einen einzelnen Aufruf von zu verwenden, grepdamit ich die --after-contextOption (oder --before-context, oder --context) verwenden kann.

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

Beispiel

Ich möchte nach übereinstimmenden needleZeilen suchen und dabei die übereinstimmenden Zeilen ignore memit einer Zeile des folgenden Kontexts ignorieren .

Hier ist meine Eingabedatei:

one needle ignore me
two
three
four needle
five

Die Ausgabe, die ich möchte, ist:

four needle
five

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

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

Antworten:


9

Wenn Sie GNU grep haben, können Sie reguläre Perl-Ausdrücke verwenden , die ein Negationskonstrukt haben .

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

Wenn Sie kein GNU-Grep haben, können Sie die Vorher / Nachher-Kontextoptionen in awk emulieren .

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}
'

8

Sie scheinen GNU . Mit GNU grep können Sie das --perl-regexFlag übergeben, um PCRE zu aktivieren, und dann eine negative Lookahead-Behauptung abgeben, Beispiel unten

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

Die Hauptsache hier ist, dass (?:(?!STRING).)*es STRINGso [^CHAR]*ist wie es istCHAR


@ 1_CR ... Sir .. es ist fantastisch ..: P etwas lächelnderack
Rahul Patil

@ RahulPatil. :-), ja GNU grep ist so gut.
iruvar

Das ist nicht ganz das, was ich will. Ich möchte, dass es funktioniert, ob "mich ignorieren" vor oder nach "Nadel" steht.
Flimm

@ RahulPatil, danke, ich habe es in der neuesten Version
behoben

Sehr hilfreich. Besonders im Fall von grep mit Kontext, in dem Sie eng übereinstimmende Linien ausschließen möchten, jedoch ohne einen bestimmten Teil des Musters. Nah an der ursprünglichen Frage, aber nicht ganz gleich.
Gaoithe

2

Ich würde empfehlen, stattdessen awk zu verwenden, da es mehrzeilige E / A besser verarbeitet. Entweder 1) Leiten Sie die Ergebnisse an GNU awk --\nals Datensatztrennzeichen weiter, oder 2) führen Sie alle Übereinstimmungen 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 nach " ignore meSet", "Set" FS=1und "Match" durchsucht , um $1nur mit der ersten Zeile zu vergleichen.

Option 2

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

Gibt es dann mehrere ignore mein der Datei, awk funktioniert nicht
Rahul Patil

@ RahulPatil: Könnten Sie Ihre Frage umformulieren oder detaillierter gestalten? Ich verstehe nicht, was Sie fragen.
Thor

@Thos testen Sie Ihr Beispiel mit dieser Eingabedatei paste.ubuntu.com/6252860
Rahul Patil

@RahulPatil: Ich verstehe, was Sie jetzt meinen. Option 1 geht davon aus, dass --\nsich zwischen jeder übereinstimmenden Gruppe ein Trennzeichen befindet, das nicht vorhanden ist, wenn die Gruppen nebeneinander liegen. Wie benachbarte Gruppen behandelt werden sollen, ist aufgabenspezifisch, daher ist dies nicht unbedingt falsch. Option 2 hängt nicht vom Trennzeichen ab und ist nicht betroffen.
Thor
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.