Extrahieren Sie einen Teil der Textdatei vom ersten Vorkommen einer Zeichenfolge bis zum ersten Vorkommen einer anderen

754
dgig

Wie kann ich einen Teil einer großen Textdatei extrahieren, beginnend beim ersten Auftreten von FOO bis zum ersten Auftreten von BAR?

In meinem Fall versuche ich, einen Teil einer von mysqldump erstellten SQL-Datei zu extrahieren.

1
Hoffe das hilft `sed -n '/ FOO /, / BAR / "Datei" Paulo vor 6 Jahren 2

2 Antworten auf die Frage

2
simlev

Dank an @dgig und @Paulo, die mir mit ihrem Feedback geholfen haben! Letzter perl Einliner hier:

perl -lne 'if(/FOO/../BAR/)' file 

Erläuterung:

if(/FOO/../BAR/){ # perform the following actions on each line, starting # with a line that contains FOO, and up to and including # a line that contains BAR  s/.*?(FOO)/$1/ if!$i++; # only on the first line that contains FOO, # delete all characters before FOO  s/BAR\K.*//&&print&&exit;# if the line contains BAR, remove characters # after BAR, print the line and stop processing  print # simply print the line contents 

Alte Antwort:

Dank an @Paulo für eine einfache sedLösung. Es ist genauso einfach und leicht zu lesen awk:

awk '/FOO/,/BAR/' file 

Es könnte jedoch zu einfach sein: Es werden ganze Zeilen zurückgegeben und nicht genau "ein Textabschnitt, der beim ersten Auftreten von FOO beginnt und beim ersten Auftreten von BAR endet". Ich glaube, dass bedeutet, dass FOO das erste Wort und BAR das letzte Wort sein sollte. Genau das zu tun, erfordert eine kompliziertere Antwort. Lassen Sie mich versuchen, das zu erreichen perl.

Einfacher Fall (gibt ganze Zeilen zurück):

perl -lne 'print if /FOO/../BAR/' file 

Komplexer Fall (genau von FOO bis BAR):

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//;print}' file 

Ich mag diese gleichwertige Lösung, die dem Bereichsoperator eine Variable zuordnet:

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*// if$a=~/E/;print}' file 

Hinweis: Es wird davon ausgegangen, dass nur ein Teil des Textes extrahiert werden muss, dh, wir sollten nach dem ersten Absatz, der durch FOO und BAR begrenzt ist, keinen anderen FOO antreffen.

Ansonsten ist der einfache Fall schon nicht mehr so ​​einfach in awk:

awk '/FOO/,/BAR/ }' file 

und in perl:

perl -lne '(print&&/BAR/&&exit) if /FOO/../BAR/' file 

Und die komplexen, verfeinerten Lösungen werden zu:

perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//&&print&&exit;print}' file 

und:

perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*//&&print&&exit if$a=~/E/;print}' file 

Dieses Beispiel zeigt, wie ein Einzeiler außergewöhnlich klar und selbsterklärend werden kann, was zu einer obskuren Folge von zufälligen Zeichen führt, da er dem Problem nur etwas mehr Komplexität hinzugefügt hat. Wo immer nötig, würde ich empfehlen, ein eigenständiges, wartbares, lesbares Skript zu schreiben, in dem zusätzliche Funktionen leicht hinzugefügt und Eckfälle berücksichtigt werden können.

Vielen Dank für diese Antworten, leider funktioniert es in meinem Fall nicht - es startet die Ausgabedatei an der richtigen Stelle, geht aber bis zum Ende der Datei durch, ohne bei 'BAR' anzuhalten. dgig vor 6 Jahren 0
Update: Versuchte deine Lösungen mit einem Text aus Gutenberg-freien Büchern - in diesem Fall funktionierte das ohne Probleme. In meinem Fall jedoch war dies eine große SQL-Datei, wie ich sie in meinem ersten Kommentar beschrieben habe. Ich bin mir nicht sicher, warum der Unterschied auftreten würde. dgig vor 6 Jahren 0
@dgid Danke für die Rückmeldung. Könnte es auf Groß- und Kleinschreibung ansprechen? Spezielle Charaktere? Bitte posten Sie einen Auszug, damit wir prüfen können, ob es sich um einen Eckpunkt handelt, an den ich nicht gedacht habe. Es fällt mir schwer, etwas anderes zu sagen. simlev vor 6 Jahren 0
@simlev Du hast recht, ich habe nicht an das erste Vorkommen gedacht, ich dachte die Linie, die das erste Vorkommen hat. Ihre Interpretation ist näher an der OP-Anfrage. Ich glaube, in Sed könnte man tun, was ohne Perl regex eine harte Arbeit ist. Ich denke, Sie müssen das 'awk' gleich nach dem Drucken der Zeile mit 'BAR' beenden, oder awk wird jeden anderen Block mit diesen Wörtern drucken, wie dies bei 'sed' der Fall ist. Paulo vor 6 Jahren 0
@Paulo Danke für das Feedback, du hast recht. simlev vor 6 Jahren 0
@dgid Bitte überprüfen Sie, ob es nach dem ersten BAR einen zweiten FOO gibt: Wie in meinem '' Hinweis: '' und, wie von Paulo richtig angegeben, wird diese Möglichkeit in meinen Lösungen nicht berücksichtigt. simlev vor 6 Jahren 0
@ Simlev Hallo! Danke für all das. Ja, es gibt definitiv ein zweites Vorkommen des Wortes, vielleicht ist dies auch darauf zurückzuführen. Meine Datei ist eine mysqldump -Datei, ähnlich diesem generischen Beispiel, das ich gefunden habe (obwohl ungefähr 1G). https://github.com/mrdavidlaing/pressupbox-development-boilerplate/blob/master/tests/wordpress.sample.sql dgig vor 6 Jahren 0
@simlev und Paulo - fantastisch - der aktualisierte Perl-One-Liner funktioniert großartig - vielen Dank! Ein echter Zeitsparer. dgig vor 6 Jahren 0
2
Paulo

In diesem Fall war es nicht so schwierig, dass ich es für möglich hielt. Mit sed, vom ersten Vorkommen von FOO bis zum ersten Vorkommen von BAR (ich habe es nicht versucht, aber wahrscheinlich wäre etwas wie das zweite FOO bis zum zweiten BAR schwieriger.)

sed -nr '/FOO/ { /FOO/ s/[^F]+FOO/FOO/p :a n /BAR/ s/([^B]+BAR).*/\1/ p /BAR/ q ba }' <<<'line1 > line2 FOO text1 FOO text2 > line3 > line4 BAR text3 BAR text4 > line5'  FOO text1 FOO text2 line3 line4 BAR 
Upvoted, obwohl es nicht wie erwartet funktioniert, wenn vor FOO ein F ist. Gierige "sed" -Begrenzung. Ich möchte Sie fragen, warum Sie sich nicht für die einfachere "/ BAR / s / (BAR). * / \ 1 /" für die zweite Auswechslung entschieden haben. simlev vor 6 Jahren 1
@simlev Über `/ BAR / s / (BAR). * / \ 1 /` du hast recht, das ist einfacher und funktioniert. Ich glaube, ich habe gerade die Substitution von `/ FOO /` nachgebildet, ich wusste nicht, dass dies nicht nötig war. Wenn es vor FOO ein "F" gibt, haben Sie auch Recht, aber diesmal bin ich mir dessen bewusst;) Ich denke in "sed", um ein Wort abzulehnen, muss man char von char "sed -nr" abstreiten. s / ([^ F] | F [^ O] | FO [^ O]) + // p '<<<' Zeile2 Foo Text FOO Text1 FOO Text2 '' Paulo vor 6 Jahren 0