Ersetzen Sie die n-te Zeile des übereinstimmenden Musters

Ersetzen Sie die n-te Zeile des übereinstimmenden Musters

Ich habe folgende Textdatei.

banana
apple
juice
mango
something

Ich suche nach dem Muster juiceund möchte die zweite Zeile dieses passenden Musters in umgekehrter Reihenfolge finden (also 2 Zeilen über dem passenden Muster) und durch ersetzen coconut.

Erwartete Ausgabe:

coconut
apple
juice
mango
something

Ich habe es mit Folgendem versucht, aber es löscht nur die beiden obigen Zeilen und nicht genau die, nach der ich suche.

tac foo.txt |sed '/juice/I,+2 d' |tac
mango
something

Ich denke, dass eine Anpassung des obigen Skripts das Problem lösen würde, bin mir aber nicht sicher.

Hinweis: Die Übereinstimmung wird nicht wiederholt und muss keine exakte Übereinstimmung sein (d. h., die Übereinstimmung kann auch in einer langen Zeile gefunden werden). Bei der Übereinstimmung muss die Groß-/Kleinschreibung beachtet werden.

Antwort1

Wenn eddas in Ordnung ist, müssen Sie eine Datei bearbeiten, keinen Stream, und es gibt nur einen  juice:

$ more <<-EOF | ed -s ./tmp.txt
	/juice/
	-2
	c
	coconut
	.
	w
	q
EOF
$

Suchen Sie die Linie, gehen Sie zwei Linien nach oben, chängen Sie, writieren Sie und  qverlassen Sie sie.


Eine noch kompaktere Variante, vorgeschlagen von @d-ben-knoble in den Kommentaren:

$ printf '%s\n' '/^juice$/-2s/.*/coconut/' w q | ed -s ./tmp.txt

Antwort2

Nach Ihrem Ansatz,

tac file|sed '/juice/{n;n;s/.*/coconut/}'|tac
  • /juice/stimmt mit einer Zeile mit überein juice.
  • n;n;druckt die aktuelle und die nächste Zeile.
  • s/.*/coconut/nimmt die Auswechslung vor.

Anscheinend hast du GNU sed, also könntest du auch verwenden, -zum die ganze Datei in den Speicher zu bekommen und direkt die Zeile zwei über juice zu bearbeiten,

sed -rz 's/[^\n]*(\n[^\n]*\n[^\n]*juice)/coconut\1/' file

[^\n]bedeutet „keine neue Zeile“ und die Klammern erfassen die durch die Rückreferenz ()reproduzierte Gruppe .\1

Antwort3

$ tac file | awk 'c&&!(--c){$0="coconut"} /juice/{c=2} 1' | tac
coconut
apple
juice
mango
something

Antwort4

Hier ist ein weiterer awkAnsatz, diesmal eine Double-Pass-Methode:

awk 'NR==FNR&&/juice/{m=FNR} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file
  • Wir geben die Datei anzweimalals Befehlszeilenargumente, sodass es zweimal verarbeitet wird.
  • Im ersten Durchgang (wobei FNR, der Zeilenzähler pro Datei, gleich NR, dem globalen Zeilenzähler ist) ermitteln wir einfach, in welcher Zeile das Suchmuster juiceauftritt, und speichern es in einer Variablen m.
  • Im zweiten Durchgang setzen wir die Zeilennummer m-2auf den Ersetzungstext coconut.
  • Als allgemeine Regel drucken wir die Zeilen einschließlich aller Änderungen, aber nur im zweiten Durchgang (wenn die Bedingung NR>FNRals „wahr“ ausgewertet wird).

Wenn Sie GNU haben awk(einige andere awkImplementierungen unterstützen dies ebenfalls), können Sie den Vorgang etwas beschleunigen, indem Sie den ersten Durchgang abbrechen, sobald die Übereinstimmung mit dem folgenden nextfileBefehl gefunden wurde:

awk 'NR==FNR&&/juice/{m=FNR;nextfile} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file

verwandte Informationen