2017-06-20 11 views
0

eine Textdatei mit der Gegeben folgende:sed: Wählen Sie Linien zwischen zwei Mustern

REGEX1 
3 - multiple line block 
4 
REGEX1 
7 - multiple line block 
REGEX1 
10 - multiple line block 

dh ich will Linien extrahieren einschließlich zwischen REGEX1 und REGEX2:

1 
2 
REGEX1 
3 - multiple line block 
4 
REGEX2 
5 
6 
REGEX1 
7 - multiple line block 
REGEX2 
8 
9 
REGEX1 
10 - multiple line block 

ich folgendes extrahieren möchten einschließlich REGEX1, aber nicht REGEX2.

Ich habe ein sed Skript geschrieben: sed -n '/REGEX1/,/REGEX2/{/REGEX2/!p}' file.

Es funktioniert gut, aber wenn eine Instanz wie folgt angegeben:

1 
2 
REGEX2 REGEX1 
3 - multiple line block 
4 
REGEX2 
5 
6 
REGEX2 REGEX1 
7 - multiple line block 
REGEX2 
8 
9 
REGEX2 REGEX1 
10 - multiple line block 

Mein Skript nur gibt mir:

3  - multiple line block 
4 
7  - multiple line block 
10 - multiple line block 

Wo, wie ich es ausgeben möchten sein:

REGEX2 REGEX1 
3 - multiple line block 
4 
REGEX2 REGEX1 
7 - multiple line block 
REGEX2 REGEX1 
10 - multiple line block 

Wie kann ich dies ohne Ineffizienz erreichen (wie Speichern von Zeilennummern und erneutes Durchlaufen der Datei)?

+1

Was ist die erwartete Ausgabe für die zweite Probe? Vielleicht suchen Sie nach Linienankern? 'sed -n '/ REGEX1 /,/REGEX2/{/^REGEX2 $ /! p}'' – Sundeep

+0

@Sundeep Ich habe meine Frage bearbeitet, um sie einzuschließen! – gitmorty

+0

na dann 'sed -n '/ REGEX1 /,/REGEX2/{/^REGEX2 $ /! P}' 'ist was du suchst? – Sundeep

Antwort

2

Schlag 1: sed ist für einfache Substitutionen an einzelnen Linien, das ist alles. Alles andere erfordert andere Konstrukte als s, g und p (mit -n), und diese wurden alle Mitte der 1970er veraltet, als awk erfunden wurde.

Strike 2: Sie sollten niemals Bereichsausdrücke verwenden, da sie triviale Aufgaben sehr geringfügig kürzer machen, aber dann eine vollständige Umschreibung oder doppelte Bedingungen erfordern, wenn die Aufgabe im geringsten interessanter wird. Verwenden Sie stattdessen Flag-Variablen.

Strike 3: sed unterstützt keine Variablen, daher können Sie keine Flags verwenden, um festzustellen, ob Sie sich in den Textblöcken befinden, die Ihnen wichtig sind.

So - nur awk verwenden:

$ awk '/REGEX2/{f=0} /REGEX1/{f=1} f' file 
REGEX1 
3 - multiple line block 
4 
REGEX1 
7 - multiple line block 
REGEX1 
10 - multiple line block 

und auf dem zweiten Satz von Eingang:

$ awk '/REGEX2/{f=0} /REGEX1/{f=1} f' file 
REGEX2 REGEX1 
3 - multiple line block 
4 
REGEX2 REGEX1 
7 - multiple line block 
REGEX2 REGEX1 
10 - multiple line block 

die oben wird mit jeder awk auf jeder UNIX-Box robust und effizient auf jede Größe Datei arbeiten .

Weitere Möglichkeiten zum Auswählen von Textblöcken finden Sie unter https://stackoverflow.com/a/17914105/1745001.

+1

Danke für die Einsicht. Ich denke, ich muss mit awk gehen – gitmorty

+0

Ja. Hoffentlich ist die Tatsache, dass es sehr offensichtlich ist, was die awk-Lösung tut, während Sie nach einer Erklärung der Sed-Lösung fragen mussten, niemandem verloren geht! –

+0

Siehe https://stackoverflow.com/help/someone-answers für die nächsten Schritte. –

0

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

sed -r '/^REGEX/h;G;s/^.*((REGEX1\b).*\n.*\2)/\1/;/\n.*REGEX1\b/P;d' file 

Shop die REGEX im Laderaum und hängen Sie ihn an folgende Aufzeichnungen. Wenn der Regexp im angehängten Teil der Zeile übereinstimmt, drucken Sie die erste Hälfte, andernfalls löschen Sie die Zeile.

BEARBEITEN:

Wechsel zur ursprünglichen Frage; die folgende einfachere Lösung erfüllt:

sed '/^REGEX1/{:a;n;/REGEX2/!ba};d' file 

Wenn jedoch die REGEX2 REGEX1 wiederholt dies geändert werden muss:

sed ':a;/^REGEX1/{:b;n;/REGEX2/!bb;ba};d' file 
+0

Es tut mir leid, ich meinte nicht die gleiche Ausgabe. Ich habe meine Frage bearbeitet, um das Gleiche zu reflektieren. Aber ich bin ziemlich fasziniert darüber, wie der obige Befehl funktioniert. Möchten Sie das erklären? – gitmorty

0

Sie können nur bandaid Ihre ursprüngliche sed ein wenig mehr.

sed -n '/REGEX1/,/REGEX2/{/REGEX1/{p;n};/REGEX2/!p}' file

Hinzufügen /REGEX1/{p;n} stellt sicher, dass REGEX1 Linien zu drucken, und dann sofort n ersetzt den Inhalt des Musterbereichs mit der nächsten Zeile.

Ich mag es nicht, wie Sie sich mit/START /,/END/wiederholen müssen, wenn es spezielle Fälle für/START/und/END/gibt, aber es scheint, als könnten Sie hier bei sed bleiben nur mit n vernünftig.

n werden Sie jedoch brennen, wenn Sie nachfolgende sed-Befehle hatten. Du könntest zu einem anderen Sed-Aufruf pipen ... oder awk verwenden.

+0

Sind Sie sicher, dass das funktioniert? Mein 'sed' klagt sed: 1: ''/ REGEX1 /,/REGEX2/{/ REGEX1/{p; ...: extra Zeichen am Ende des n Befehls. " – gitmorty

+0

Ich bin gerade auf OSX, das könnte also das Problem sein. Der Befehl funktioniert gut mit GNU 'sed'. Aber es wäre schön zu wissen, wie man es mit dem BSD 'sed' auch macht. – gitmorty

+0

@AkhilAvinash OSX/BSD sed will nur ein Semikolon nach dem letzten Befehl in der '{}' – stevesliva

Verwandte Themen