Wie kann ich einen Teil einer großen Textdatei extrahieren, beginnend beim ersten Vorkommen von FOO und endend beim ersten Vorkommen von BAR?
In meinem Fall versuche ich, einen Teil einer von mysqldump erstellten SQL-Datei zu extrahieren.
Antwort1
Anerkennung an@dgigUnd@Paulodie mir mit ihrem Feedback geholfen haben!Finale perl
Einzeiler hier:
perl -lne 'if(/FOO/../BAR/){s/.*?(FOO)/$1/ if!$i++;s/BAR\K.*//&&print&&exit;print}' 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:
Anerkennung an@Paulofür eine einfache sed
Lösung. Es ist genauso einfach und leicht zu lesen awk
:
awk '/FOO/,/BAR/' file
Es könnte jedoch zu einfach sein: Es gibt ganze Zeilen zurück und nicht genau „einen Textabschnitt, der beim ersten Vorkommen von FOO beginnt und beim ersten Vorkommen von BAR endet“. Ich glaube, das bedeutet, dass FOO das erste Wort und BAR das letzte sein sollte. Genau das zu tun, erfordert eine kompliziertere Antwort. Lassen Sie mich versuchen, dies in 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
Mir gefällt diese gleichwertige Lösung, die dem Bereichsoperator eine Variable zuweist:
perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*// if$a=~/E/;print}' file
Notiz:Es wird davon ausgegangen, dass nur ein Textabschnitt extrahiert werden muss, d. h. wir sollten nach dem ersten durch FOO und BAR abgegrenzten Absatz kein weiteres FOO vorfinden.
Ansonsten ist der einfache Fall schon nicht mehr so einfach in awk
:
awk '/FOO/,/BAR/ {print; if ($0~/BAR/) {exit} }' file
und in perl
:
perl -lne '(print&&/BAR/&&exit) if /FOO/../BAR/' file
Und die komplexeren, ausgefeilteren Lösungen werden:
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 von außergewöhnlich klar und selbsterklärend zu einer scheinbar obskuren Folge zufälliger Zeichen werden kann, nur weil er das Problem ein wenig komplexer gemacht hat. Wo immer nötig, würde ich empfehlen, ein eigenständiges, wartbares und lesbares Skript zu schreiben, in dem zusätzliche Funktionen leicht hinzugefügt und Sonderfälle berücksichtigt werden können.
Antwort2
In diesem Fall war es nicht so schwierig, wie ich dachte. Mit sed
, vom ersten Vorkommen von FOO bis zum ersten Vorkommen von BAR (ich habe es nicht versucht, aber wahrscheinlich wäre so 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