So wählen Sie Linien zwischen zwei Markierungsmustern aus, die bei awk / sed mehrfach auftreten können


119

Verwenden awkoder sedwie kann ich Linien auswählen, die zwischen zwei verschiedenen Markierungsmustern auftreten? Es können mehrere Abschnitte mit diesen Mustern markiert sein.

Zum Beispiel: Angenommen, die Datei enthält:

abc
def1
ghi1
jkl1
mno
abc
def2
ghi2
jkl2
mno
pqr
stu

Und das Startmuster ist abcund das Endmuster ist mno Also, ich brauche die Ausgabe als:

def1
ghi1
jkl1
def2
ghi2
jkl2

Ich benutze sed, um das Muster einmal anzupassen:

sed -e '1,/abc/d' -e '/mno/,$d' <FILE>

Gibt es eine Möglichkeit in sedoder awk es bis zum Ende der Datei wiederholt zu tun?

Antworten:


188

Verwenden Sie diese Option awkmit einem Flag, um den Druck bei Bedarf auszulösen:

$ awk '/abc/{flag=1;next}/mno/{flag=0}flag' file
def1
ghi1
jkl1
def2
ghi2
jkl2

Wie funktioniert das?

  • /abc/stimmt mit Zeilen mit diesem Text überein /mno/.
  • /abc/{flag=1;next}Legt fest, flagwann der Text abcgefunden wird. Dann wird die Zeile übersprungen.
  • /mno/{flag=0}Deaktiviert das, flagwenn der Text mnogefunden wird.
  • Das Finale flagist ein Muster mit der Standardaktion print $0: Wenn flaggleich 1 ist, wird die Zeile gedruckt.

Eine detailliertere Beschreibung und Beispiele sowie Fälle, in denen die Muster entweder angezeigt werden oder nicht, finden Sie unter Auswählen von Linien zwischen zwei Mustern. .


30
Wenn Sie alles zwischen und einschließlich des Musters drucken möchten, können Sie verwenden awk '/abc/{a=1}/mno/{print;a=0}a' file.
Scai

6
Ja, @scai! oder sogar awk '/abc/{a=1} a; /mno/{a=0}' file- wenn Sie die aBedingung vor das setzen /mno/, wird die Zeile vor dem Setzen als wahr bewertet (und gedruckt) a=0. Auf diese Weise können wir das Schreiben vermeiden print.
Fedorqui 'SO hör auf,'

12
@scai @fedorqui Um Musterausgabe einzuschließen, können Sie tunawk '/abc/,/mno/' file
Jotne

1
@hkasera awk '/abc/{flag=1}/mno/{flag=0}flag' filesollte machen.
Fedorqui 'SO hör auf zu schaden'

2
@EirNym, das ist ein seltsames Szenario, das auf sehr unterschiedliche Weise behandelt werden kann: Welche Zeilen möchten Sie drucken? awk 'flag; /PAT1/{flag=1; next} /PAT1/{flag=0}' fileWürde wahrscheinlich machen.
Fedorqui 'SO hör auf zu schaden'

45

Verwenden von sed:

sed -n -e '/^abc$/,/^mno$/{ /^abc$/d; /^mno$/d; p; }'

Die -nOption bedeutet, dass standardmäßig nicht gedruckt wird.

Das Muster sucht nach Zeilen, die nur abczu gerade enthalten mno, und führt dann die Aktionen in der aus { ... }. Die erste Aktion löscht die abcZeile. der zweite die mnoLinie; und pdruckt die restlichen Zeilen. Sie können die Regexes nach Bedarf entspannen. Zeilen außerhalb des Bereichs von abc.. mnowerden einfach nicht gedruckt.


Danke für die Antwort und für die Erklärung! :)
dvai

@ JonathanLeffler kann ich wissen, was der Zweck der Verwendung ist-e
Kasun Siyambalapitiya

1
@ KasunSiyambalapitiya: Meistens bedeutet es, dass ich es gerne benutze. Formal gibt es an, dass das nächste Argument (Teil) des Skripts ist, seddas ausgeführt werden soll. Wenn Sie mehrere Argumente verwenden möchten oder müssen, um das gesamte Skript einzuschließen, müssen Sie -evor jedem dieser Argumente verwenden. Andernfalls ist es optional (aber explizit).
Jonathan Leffler

@ JonathanLeffler Danke
Kasun Siyambalapitiya

Nett! (Ich bevorzuge sed gegenüber awk.) Wenn Sie komplexe reguläre Ausdrücke verwenden, wäre es schön, sie nicht wiederholen zu müssen. Ist es nicht möglich, die erste / letzte Zeile des "ausgewählten" Bereichs zu löschen? Oder um das zuerst dauf alle Zeilen bis zum ersten Spiel und dann dauf alle Zeilen anzuwenden, die mit dem zweiten Spiel beginnen?
Hans_meine

18

Dies könnte für Sie funktionieren (GNU sed):

sed '/^abc$/,/^mno$/{//!b};d' file

Löschen Sie alle Zeilen mit Ausnahme der Zeilen zwischen abcundmno



Das ist fantastisch. Das {//!b}verhindert, dass das abcund mnoin die Ausgabe aufgenommen wird, aber ich kann nicht herausfinden, wie. Könntest du erklären?
Brendan

1
@Brendan Die Anweisung //!blautet: Wenn die aktuelle Zeile keine der Zeilen ist, die dem Bereich entsprechen, unterbrechen Sie diese Zeilen und drucken Sie sie aus. Andernfalls werden alle anderen Zeilen gelöscht.
Potong

13
sed '/^abc$/,/^mno$/!d;//d' file

Golf zwei Zeichen besser als ppotong {//!b};d

Die leeren Schrägstriche //bedeuten: "Den zuletzt verwendeten regulären Ausdruck wiederverwenden". und der Befehl macht dasselbe wie der verständlichere:

sed '/^abc$/,/^mno$/!d;/^abc$/d;/^mno$/d' file

Dies scheint POSIX zu sein :

Wenn eine RE leer ist (dh kein Muster angegeben ist), verhält sich sed so, als ob die letzte RE angegeben wurde, die im zuletzt angewendeten Befehl verwendet wurde (entweder als Adresse oder als Teil eines Ersatzbefehls).


1
Ich denke, die zweite Lösung wird nichts bringen, da der zweite Befehl ebenfalls ein Bereich ist. Allerdings ein großes Lob für die erste.
Potong

@potong wahr! Ich muss mehr lernen, warum der erste funktioniert. Vielen Dank!
Ciro Santilli 法轮功 冠状 病 六四 事件 13

7

Aus den Links der vorherigen Antwort ging hervor, dass dies für mich unter kshSolaris der Fall war:

sed '1,/firstmatch/d;/secondmatch/,$d'
  • 1,/firstmatch/d: firstmatchLöschen Sie von Zeile 1 bis zum ersten Auffinden .
  • /secondmatch/,$d: vom ersten Auftreten secondmatchbis zum Ende der Datei löschen.
  • Semikolon trennt die beiden Befehle, die nacheinander ausgeführt werden.

Nur neugierig, warum kommt der Bereichsbegrenzer ( 1,) vorher /firstmatch/? Ich vermute, das könnte auch formuliert werden '/firstmatch/1,d;/secondmatch,$d'?
Luke Davis

2
Mit "1, / firstmatch / d" sagen Sie "von Zeile 1 bis zum ersten Mal, wenn Sie 'firstmatch' finden, löschen". Während Sie mit "/ secondmatch /, $ d" sagen "vom ersten Auftreten von 'secondmatch' bis zum Ende der Datei löschen". Das Semikolon trennt die beiden Befehle, die nacheinander ausgeführt werden.
FanDeLaU

2
perl -lne 'print if((/abc/../mno/) && !(/abc/||/mno/))' your_file

Gut zu wissen, Perl-Äquivalent, da es eine ziemlich gute Alternative zu awk und sed ist.
Akhan

2

so etwas funktioniert bei mir:

file.awk:

BEGIN {
    record=0
}

/^abc$/ {
    record=1
}

/^mno$/ {
    record=0;
    print "s="s;
    s=""
}

!/^abc|mno$/ {
    if (record==1) {
        s = s"\n"$0
    }   
}

mit: awk -f file.awk data...

edit: O_o fedorqui Lösung ist viel besser / hübscher als meine.


3
In GNU if (record=1)sollte awk sein if (record==1), dh doppelt = - siehe gawk Vergleichsoperatoren
George Hawkins

2

Don_crisstis Antwort von Nur Text zwischen 2 übereinstimmenden Mustern anzeigen ?

firstmatch="abc"
secondmatch="cdf"
sed "/$firstmatch/,/$secondmatch/!d;//d" infile

Das ist viel effizienter als die Anwendung von AWK, siehe hier .


Ich halte es nicht für sinnvoll, die Zeitvergleiche zu verknüpfen, da die Anforderungen der Fragen sehr unterschiedlich sind, daher die Lösungen.
fedorqui 'SO hör auf,'

2
Ich bin anderer Meinung, weil wir einige Kriterien haben sollten, um Antworten zu vergleichen. Nur wenige haben SED-Anwendungen.
Léo Léopold Hertz 준영

0

Ich habe versucht, awkLinien zwischen zwei Mustern zu drucken, während Muster2 auch Muster1 entspricht . Außerdem sollte die Zeile pattern1 gedruckt werden.

zB Quelle

package AAA
aaa
bbb
ccc
package BBB
ddd
eee
package CCC
fff
ggg
hhh
iii
package DDD
jjj

sollte eine Ausgabe von haben

package BBB
ddd
eee

Wo Muster1 ist package BBB, ist Muster2 package \w*. Beachten Sie, dass dies CCCkein bekannter Wert ist und daher nicht buchstäblich abgeglichen werden kann.

In diesem Fall funktioniert weder @scai awk '/abc/{a=1}/mno/{print;a=0}a' filenoch @fedorqui awk '/abc/{a=1} a; /mno/{a=0}' filefür mich.

Schließlich habe ich es geschafft, es zu lösen awk '/package BBB/{flag=1;print;next}/package \w*/{flag=0}flag' file, haha

Ein wenig mehr Aufwand awk '/package BBB/{flag=1;print;next}flag;/package \w*/{flag=0}' fileführt dazu, dass auch die Zeile pattern2 gedruckt wird, d. H.

package BBB
ddd
eee
package CCC
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.