Drucken Sie Zeile X vor / nach der gefundenen Zeile

7

Ich möchte in einer bestimmten Datei nach einer bestimmten Zeichenfolge suchen. Wenn ich die Zeichenfolge gefunden habe, möchte ich auch die Zeilen der Zeile X vor (oder nach) dieser Zeile drucken.

Kann das mit grep oder awk oder brauche ich eine Kombination?

Ich möchte so etwas wie haben , diese aber nicht mit allen Hinter / führenden Linien vor oder nach einem Treffer, nur der X - te.

Zum Beispiel, wenn meine Eingabe so aussieht:

line1 with a pattern
line2
line3
line4 with a pattern
line5
line6
line7 with a pattern
...

Ich möchte zB nach dem Wort 'Muster' suchen und diese Zeile + die Zeile ausgeben, die 2 Zeilen danach ist, aber nicht die Zeile, die direkt auf die Zeile mit dem Muster folgt. Die gewünschte Ausgabe ist also:

line1 with a pattern
line3
line4 with a pattern
line6
line7 with a pattern
...
MaVe
quelle
awkist so allgemein, dass Sie es verwenden können, um Ihre Aufgabe zu lösen, aber Sie müssten ein Skript schreiben. In diesem Fall wäre die Antwort also ja . Wie Sie bereits herausgefunden haben, grepkönnen Sie Ihre Aufgabe bis zu einem gewissen Grad erledigen, aber vielleicht nicht alles. Warum konzentrieren Sie sich auf diese beiden Tools, erwähnen sie aber sedin Ihren Tags? Ist Ihre eigentliche Frage: Kann ich dies nur mit Standard-GNU-Tools ohne Skripterstellung tun / kombinieren? Die Antwort lautet wahrscheinlich auch Ja, kann aber aufgrund von Sonderfällen etwas kniffliger sein: Was ist beispielsweise die gewünschte Ausgabe, wenn das Muster in den ersten X- oder letzten X-Zeilen gefunden wird?
Marcus Rickert
@MarcusRickert Ja, die eigentliche Frage ist, ob ich dies mit GNU-Tools ohne Skripterstellung tun kann. In Bezug auf die erste oder letzte Zeile: Die Ausgabe ist konsistent. Wenn also ein Muster angezeigt wird, werden sicher die erforderlichen X-Zeilen folgen. Ich werde der Frage ein Beispiel hinzufügen.
MaVe

Antworten:

15

grepwird dies für Sie tun, mit Optionen -A(nach) und -B(vor) und -C(Kontext). Ein Beispiel, das ich oft benutze, ist:

   sudo lspci -vnn | grep -i net -A 12

da dieser Befehl nach dem Spiel 12 Zeilen anzeigt, einschließlich des Treibers, der zur Steuerung der Netzwerkkarte (-i net) verwendet wird. Im Allgemeinen lautet der Befehl:

  grep text_to_search -A n -B m file.extension

Hiermit werden mZeilen vor dem Spiel und nZeilen nach dem Spiel ausgegeben . Oder Sie können verwenden

 grep text_to_search -C n file.extension

um insgesamt nZeilen zu zeigen , die den gefundenen Text umgeben (halb vor, halb nach dem Spiel).

MariusMatutiae
quelle
1
Ihr Beitrag (upvoted) ist größtenteils korrekt, außer dass das Flag -C n nicht Ihren Angaben entspricht. -C n ist äquivalent zu -A n -B n und zeigt n Kontextlinien auf beiden Seiten der Übereinstimmung.
Jim L.
3

Nachdem ich mich ein wenig mit dieser Frage befasst habe, denke ich, dass ich meinen Kommentar zur Machbarkeit mit Standard-GNU-Tools ohne Skripten überarbeiten muss. Dies kann aufgrund der Sonderfälle sehr schwierig sein.

Wenn es Ihnen nichts ausmacht, awk zu verwenden, könnte ich die folgende Lösung anbieten. Dieses Skript context.awkist immer noch ziemlich prägnant:

{
    lines[NR] = $0
    if (dump[NR]) {
        print $0;
    if ($0 ~ Pattern) {
        if (NR-Delta in lines) {
            print "---"
            print lines[NR-Delta]
        }
        dump[NR+Delta] = 1
        print $0;
    }
    if (NR-Delta in lines) 
        delete lines [NR-Delta];
}

Es muss wie folgt aufgerufen werden:

awk -v Delta=X -v Pattern=PATTERN -f context.awk sample.txt

wo Xist die gewünschte "Kontextentfernung" und PATTERNdas Suchmuster. Das Skript versucht, mehrere Musterkontexte in der Datei zu trennen, indem Zeilen ---dazwischen gedruckt werden . So zum Beispiel das Folgendesample.txt

line1
line2
line3 XXX
line4
line5
line6
line7 XXX
line8
line9
line10 XXX
line11

mit diesem Aufruf

awk -v Delta=3 -v Pattern=XXX -f context.awk sample.txt

ergibt die folgende Ausgabe

line3 XXX
line6
---
line4
line7 XXX
line10 XXX
---
line7 XXX
line10 XXX
Marcus Rickert
quelle
Natürlich könnte das Skript auch in den Aufruf integriert werden, wenn awk.
Marcus Rickert
2

Der folgende sedBefehl scheint für den von Ihnen beschriebenen Fall zu funktionieren, den ich neu formulieren würde: Drucken Sie die Zeile, die meinem Muster entspricht, drucken Sie nicht die unmittelbar folgende Zeile, drucken Sie auch die Zeile zwei Zeilen nach meinem Muster und suchen Sie dann weiter nach Musterübereinstimmungen.

sed -n -e '/with a pattern/ {h;n;n;H;x;p}' file

Es scheint ein bisschen hässlich zu sein, aber durch Hinzufügen zusätzlicher Befehle n(Zeile überspringen, zur nächsten wechseln) und H(diese Zeile durch Anhängen an den Haltepuffer beibehalten) können Sie nach einer Musterübereinstimmung eine beliebige Keep / Skip-Beziehung erstellen.

Beachten Sie, dass wir nach der Übereinstimmung zunächst die übereinstimmende Zeile mit dem hBefehl in den Haltepuffer kopieren . Schließlich tauschen wir den Halte- und Musterraum mit aus xund drucken den Musterraum mit p. An diesem Punkt sedwird die Datei zeilenweise weiter verarbeitet, um nach einer anderen Übereinstimmung zu suchen.

Eine praktische Referenz für sedBefehle finden Sie hier .

Deaks
quelle
0

grep -A2 <pattern> file | grep -B1 <pattern> | grep -v "\-\-" funktioniert bei mir:

user@box /tmp $ grep -A2 "with a pattern" test.txt | grep -B1 "with a pattern" | grep -v "\-\-"
line1 with a pattern
line3
line4 with a pattern
line6
line7 with a pattern

user@box /tmp $ cat test.txt 
line1 with a pattern
line2
line3
line4 with a pattern
line5
line6
line7 with a pattern
CHK
quelle