Suchen und Ersetzen in Sed mit Mehrzeilenmuster

3797
Toc

Ich habe eine Datei mit folgendem Inhalt:

alfa [many lines here] TAG1 TAG2  bravo TAG3  charlie TAG4 [many lines here] 

Dabei sind TAG1, TAG2, TAG3 und TAG4 feste Strings und alfa, bravo und charlie wechseln von Zeit zu Zeit, und ich möchte Folgendes extrahieren:

alfa-bravo-charlie 

Was ist der genaue sed-Befehl, den ich verwenden muss? Ich weiß nicht, wie man mit mehrzeiligem Muster arbeitet. :(

PS: Ich benutze sed für Windows.

2

2 Antworten auf die Frage

3
dubiousjim

Das funktioniert mit gnu sed, ich glaube nicht, dass es auf gnu-spezifischen Erweiterungen beruht, aber ich weiß es nicht.

echo "$yourdata" | sed -ne '1; /^TAG1$/ } } }' 

Ergebnis: alfa-bravo-charlie

Wie funktioniert es? Zuerst sagen wir sed "-n", dass wir nichts drucken wollen, wenn wir nicht ausdrücklich [p] rint sagen.

Der erste Block des sed-Ausdrucks ist "1 ". Dies besagt, wenn wir Zeile 1 lesen, diese Zeile in den [h] alten Puffer stapeln und dann [d] aus dem Arbeitspuffer löschen, so dass wir die nächste Zeile lesen und von Anfang an durch den sed-Ausdruck gehen.

Beim Lesen nachfolgender Zeilen wird der Block "1 {...}" übersprungen.

Wir passen nichts weiter zusammen, bis wir die Linie TAG1 erreichen. An dieser Stelle führen wir den langen {...} Block aus. Dies sagt, dass zuerst die [n] ext-Zeile gelesen wird, wodurch die TAG1-Zeile überschrieben wird, die sich im Puffer befand. Wenn der Puffer jetzt TAG2 ist, führen wir den nächsten inneren {...} - Block aus. Das liest zuerst die [n] ext-Zeile und überschreibt, was sich bereits im Puffer befindet. Die nächsten zwei Befehle sind "N; N". Lesen Sie die nächsten 2 Zeilen, aber fügen Sie sie hinzusie in den Arbeitspuffer, anstatt ihn zu überschreiben. Wenn der Arbeitspuffer jetzt mit / \ nTAG3 $ / übereinstimmt, führen wir den nächsten inneren {...} - Block aus. Das heißt zuerst "s ///", das heißt, ersetzen Sie den zuletzt übereinstimmenden Ausdruck durch die leere Zeichenfolge. Dadurch wird das "\ nTAG3" am Ende des Arbeitspuffers gelöscht und "\ nbravo" verlassen. Dann machen wir [H], was das an den Haltepuffer anhängt. ([h] überschreibt den Haltepuffer, [H] hängt daran an). Nun enthält der Haltepuffer die erste Zeile "alfa", dann die nächste Zeile "\ nbravo". Diese werden durch eine neue Zeile ergänzt, so dass wir wirklich "alfa \ n \ nbravo" haben. Wir werden uns später um die beiden Zeilen kümmern.

Wir machen weiter, bis wir "alfa \ n \ nbravo \ n \ ncharly" im Haltepuffer haben. Dann sagen wir [g] und den Haltepuffer (überschreiben, was sich im Arbeitspuffer befindet). Wir machen ein "s / \ n \ n / - /", um die doppelten Zeilenumbrüche in Striche zu verwandeln. Wir fügen am Ende des [s] -Befehls "g" - und "p" -Flags hinzu, so dass die Ersetzung global funktioniert (dh nicht nur eine Ersetzung dann stoppt) und dass das Ergebnis nach der Ersetzung [p] rinted ist.

Dann müssen wir den Rest des Eingabestroms nicht lesen.

1
larsks

Aus Ihrem Beispiel geht nicht genau hervor, was Sie zu tun versuchen. Es klingt so, als würden Sie versuchen, den gesamten Inhalt der Datei zu löschen, mit Ausnahme von drei Markierungen, die Sie zusammenfügen möchten. Sie brauchen dafür kein sed, Sie können einfach Folgendes eingeben:

echo alfa-bravo-charlie 

Und du hast dein Ziel erreicht. Wenn Sie einfach den Inhalt zwischen "alfa" und "charlie" entfernen möchten, können Sie ein sed-Skript wie folgt verwenden:

/charlie/ a\ alfa-bravo-charlie /alfa/,/charlie/ d 

Wenn Sie dies nicht möchten, kann es hilfreich sein, wenn Sie Ihr Beispiel erläutern.