Wie man das Ende der Linie mit sed erkennt

12

Ich suche nach einer Möglichkeit, das Ersetzen nur auszuführen, wenn das letzte Zeichen eine neue Zeile ist sed.

Zum Beispiel:

lettersAtEndOfLine

wird ersetzt, aber das ist nicht:

lettersWithCharacterAfter&

Da sedes mit Zeilenumbrüchen nicht gut funktioniert, ist es nicht so einfach wie

$ sed -E "s/[a-zA-Z]*\n/replace/" file.txt

Wie kann dies erreicht werden?

Matthew D. Scholefield
quelle

Antworten:

18

Mit Standard sehen sedSie niemals eine neue Zeile im Text, der aus einer Datei gelesen wird. Dies liegt daran, dass sedzeilenweise gelesen wird und daher am Ende des Textes der aktuellen Zeile im Musterbereich keine neue Zeile steht sed. Mit anderen Worten, sedliest durch Zeilenumbrüche getrennte Daten, und die Trennzeichen sind nicht Teil dessen, was ein sedSkript sieht.

Reguläre Ausdrücke können am Ende der Zeile mit (oder am Anfang mit ) verankert$ werden ^. Wenn Sie einen Ausdruck am Anfang / Ende einer Zeile verankern, wird er genau dort und nicht nur irgendwo in der Zeile angezeigt.

Wenn Sie etwas, das mit dem Muster [A-Za-z]*am Ende der Zeile übereinstimmt, durch etwas ersetzen möchten , verankern Sie das Muster wie folgt:

[A-Za-z]*$

... erzwingt, dass es am Ende der Zeile und nirgendwo anders übereinstimmt.

Da jedoch [A-Za-z]*$auch nichts übereinstimmt (z. B. die leere Zeichenfolge am Ende jeder Zeile), müssen Sie den Abgleich von etwas erzwingen , z. B. durch Angabe

[A-Za-z][A-Za-z]*$

So wird Ihre sed Kommandozeile also sein

$ sed 's/[A-Za-z][A-Za-z]*$/replace/' file.txt

Ich habe den -ESchalter hier nicht verwendet , weil er nicht benötigt wird. Damit hättest du schreiben können

$ sed -E 's/[A-Za-z]+$/replace/' file.txt

Es ist Geschmackssache.

Kusalananda
quelle
Obwohl ich wusste, wie man das macht, erhalten Sie eine +1, nur wenn Sie den Fachbegriff dafür verwenden. :) Das nennt man also Verankerung - schön zu wissen. Bis jetzt musste ich es immer umschreiben ... Ein weiterer Hinweis+ zum Thema : Sie können es auch ohne erweiterten regulären Ausdruck verwenden. Denken Sie daran, es so zu schreiben \+. Funktioniert also sed -e 's/[A-Za-z]\+$/replace/' file.txtauch ohne sedinstallierte GNU einwandfrei . Und nicht zu vergessen: Nicht verwenden -E, da GNU seddies nicht unterstützt .
Syntaxfehler
1
@syntaxerror - Ich denke, Sie können den letzten Satz entfernen oder zumindest aufheben, da dies gnu seddefinitiv unterstützt wird-E .
don_crissti
@don_crissti Nun, ich dachte, Sie sind lange genug in diesem Netzwerk, um zu wissen, dass es keine Möglichkeit gibt, Teile eines Kommentars aufzuheben (es sei denn, Sie schreiben ihn vollständig neu). Lassen Sie mich also Folgendes korrigieren: GNU unterstützt sedmöglicherweise "stillschweigend" -E, dies ist jedoch weder in der Manpage (noch im Texinfo-Handbuch (beide aktiviert)) dokumentiert . Daher habe ich angenommen, dass es nicht unterstützt wird (was schließlich eine falsche Annahme war). Wie auch immer, Sie haben Recht, denn zumindest wird sich GNU sednicht beschweren, wenn Sie diese Option verwenden.
Syntaxfehler
@don_crissti Schön, dass du es getan hast! So zumindest hat sich bestätigt, dass sed wird eine bestimmte Option nehmen , die noch nicht richtig dokumentiert worden. Das ist immer praktisch; Wenn niemand über den Mangel an Dokumentation informiert ist, wird niemand dies jemals beheben.
Syntaxfehler
@syntaxerror, siehe unix.stackexchange.com/a/310454/135943 . Wenn Sie mit alten Systemen wie RHEL 5 arbeiten müssen, verwenden Sie natürlich eine GNU sed-Version, die nicht unterstützt wird -E.
Wildcard
3
sed "s/[a-zA-Z]*$/replace/" input.txt > result.txt

Oder der lange komplexe unnötige Weg:

Ich habe herausgefunden, dass dies mit Hilfe von tr unter Verwendung von sed möglich ist. Sie können ein anderes Zeichen zuweisen, um das Ende der Zeile darzustellen. Ein anderes temporäres Zeichen muss verwendet werden, in diesem Fall "` ". Verwenden wir "~", um das Ende der Zeile darzustellen:

tr '\n' '`' <input.txt >output.txt
sed -i "s/`/~`/" output.txt
tr '`' '\n' <output.txt >result.txt

Verwenden Sie dann zum Ausführen der eigentlichen Suche und zum Ersetzen "~" anstelle von "\ n":

sed -i -E "s/[a-zA-Z]*~/replace/" result.txt

Und dann bereinigen Sie das zusätzliche Zeichen in den anderen Zeilen:

sed -i "s/~//" result.txt

Offensichtlich kann dies alles zusammen geleitet werden, was zu etwas führt wie:

tr '\n' '`' <input.txt | sed -e "s/`/~`/" | tr '`' '\n' | sed -E -e "s/[a-zA-Z]*~/replace/" | sed "s/~//" > result.txt
Matthew D. Scholefield
quelle
3
Ich bin mir nicht sicher, ob ich das verstehe ... Warum verankerst du dich nicht einfach am Ende der Linie $? zBs/[a-zA-Z]*$/replace/
don_crissti
1
2 Punkte: 1) Du solltest besser nutzen \+statt , *da diese ermöglichen Null Buchstaben am Ende der Zeichenfolge; 2) Sie können eine Zeichenklasse verwenden [[:alpha:]]. Also:sed 's/[[:alpha:]]\+$/replace/' file
Glenn Jackman
@glennjackman Wofür ist der Backslash vor dem Plus? Würde das nicht zum Zusatzzeichen passen?
Matthew D. Scholefield
1
GNU sed ohne diese -rOption verwendet diese Syntax für reguläre Ausdrücke .
Glenn Jackman
0

Aus dem (defekten) Code-Snippet, das Sie gepostet haben, möchten Sie anscheinend auch die neue Zeile ersetzen. In diesem Fall kann Ihnen die Regex-Verankerung an sich nicht helfen. Folgendes ist eine Lösung:

sed '/[[:alpha:]]\+$/{N;s/[[:alpha:]]\+\n/replace/}' your_file

Heruntergebrochen:

  • /[a-zA-Z]\+$/{} bedeutet, dass alles, was in den Locken enthalten ist, auf Linien angewendet wird, die mit dem regulären Ausdruck übereinstimmen.
  • Der Regex ist derjenige, der die Verankerung verwendet, wie in Ihrer eigenen Antwort gezeigt , modifiziert, um die Kommentare von Glenn Jackman zu berücksichtigen.
  • Innerhalb der Locken Nbedeutet "die nächste Zeile an den aktiven Puffer anhängen" (was sedals "Musterraum" bezeichnet wird)
  • Schließlich ist die s///Aussage Ihre erforderliche Ersetzung. Es funktioniert jetzt, weil der Musterraum zwei aufeinanderfolgende Zeilen enthält und die neue Zeile daher ein Teil davon ist.
Joseph R.
quelle
0

Um das Zeilenende zu finden, verwenden Sie einfach das $ -Zeichen :

Ohne Zeilenende Anker:

sed -n '/pattern/p' file 

Ohne Zeilenende Anker:

sed -n '/pattern$/p' file
Benutzer unbekannt
quelle