Ein Überblick über die vielen hilfreichen Antworten , ergänzt durch Erklärungen :
In den Beispielen wird ein vereinfachter Anwendungsfall verwendet: Ersetzen Sie das Wort "foo" nur in der ersten übereinstimmenden Zeile durch "bar".
Aufgrund der Verwendung von ANSI C-Strings in Anführungszeichen ( $'...'
) die Probeneingangsleitungen zu liefern, bash
, ksh
, oder zsh
wird als Shell angenommen.
sed
Nur GNU :
Die Antwort von Ben Hoffstein zeigt uns, dass GNU eine Erweiterung der POSIX-Spezifikationsed
bereitstellt , die die folgende 2-Adressen-Form zulässt : 0,/re/
( re
stellt hier einen beliebigen regulären Ausdruck dar).
0,/re/
Ermöglicht, dass der reguläre Ausdruck auch in der ersten Zeile übereinstimmt . Mit anderen Worten: Eine solche Adresse erstellt einen Bereich von der ersten Zeile bis einschließlich der übereinstimmenden Zeile re
- unabhängig davon, ob sie re
in der ersten Zeile oder in einer nachfolgenden Zeile auftritt.
- Vergleichen Sie dies mit dem POSIX-kompatiblen Formular
1,/re/
, das einen Bereich erstellt, der von der ersten Zeile bis einschließlich der Zeile re
in den nachfolgenden Zeilen übereinstimmt . Mit anderen Worten: Dies erkennt nicht das erste Auftreten einer re
Übereinstimmung, wenn es in der ersten Zeile auftritt , und verhindert auch die Verwendung einer Kurzform//
für die Wiederverwendung des zuletzt verwendeten regulären Ausdrucks (siehe nächster Punkt). 1
Wenn Sie eine 0,/re/
Adresse mit einem s/.../.../
(Ersetzungs-) Aufruf kombinieren , der denselben regulären Ausdruck verwendet, führt Ihr Befehl die Ersetzung effektiv nur in der ersten Zeile durch, die übereinstimmt re
.
sed
bietet eine praktische Verknüpfung für die Wiederverwendung des zuletzt angewendeten regulären Ausdrucks : ein leeres Trennzeichenpaar//
.
$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Nur POSIX-Funktionen sed
wie BSD (macOS)sed
(funktionieren auch mit GNU sed
):
Da 0,/re/
es nicht verwendet werden kann und das Formular 1,/re/
nicht erkennt , re
ob es in der ersten Zeile auftritt (siehe oben), ist eine spezielle Behandlung für die erste Zeile erforderlich .
Die Antwort von MikhailVS erwähnt die Technik, die hier in ein konkretes Beispiel gebracht wird:
$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Hinweis:
Die leere Regex- //
Verknüpfung wird hier zweimal verwendet: einmal für den Endpunkt des Bereichs und einmal im s
Aufruf; In beiden Fällen wird Regex foo
implizit wiederverwendet, sodass wir ihn nicht duplizieren müssen, was sowohl zu kürzerem als auch zu wartbarem Code führt.
POSIX sed
benötigt nach bestimmten Funktionen tatsächliche Zeilenumbrüche, z. B. nach dem Namen eines Etiketts oder sogar nach dessen Auslassung, wie dies t
hier der Fall ist. Die strategische Aufteilung des Skripts in mehrere -e
Optionen ist eine Alternative zur Verwendung eines tatsächlichen Zeilenumbruchs: Beenden Sie jeden -e
Skriptabschnitt dort, wo normalerweise ein Zeilenumbruch erforderlich ist.
1 s/foo/bar/
wird nur foo
in der 1. Zeile ersetzt, wenn dort gefunden. Wenn ja, t
verzweigt sich zum Ende des Skripts (überspringt verbleibende Befehle in der Zeile). (Die t
Funktion verzweigt nur dann zu einem Label, wenn der letzte s
Aufruf eine tatsächliche Ersetzung durchgeführt hat. Wenn kein Label vorhanden ist, wie hier der Fall, wird das Ende des Skripts zu verzweigt.)
In diesem Fall stimmt die Bereichsadresse 1,//
, die normalerweise das erste Vorkommen ab Zeile 2 findet, nicht überein, und der Bereich wird nicht verarbeitet, da die Adresse ausgewertet wird, wenn die aktuelle Zeile bereits vorhanden ist 2
.
Wenn umgekehrt in der ersten Zeile keine Übereinstimmung vorhanden ist, 1,//
wird diese eingegeben und die wahre erste Übereinstimmung gefunden.
Der Nettoeffekt ist das gleiche wie bei GNU sed
‚s 0,/re/
: nur dem ersten Vorkommen ersetzt wird , ob es auf der 1. Zeile oder ein anderes auftritt.
NON-Range-Ansätze
Potongs Antwort zeigt Schleifentechniken , die die Notwendigkeit einer Reichweite umgehen . Da er die GNU- sed
Syntax verwendet, sind hier die POSIX-kompatiblen Entsprechungen :
Schleifentechnik 1: Führen Sie beim ersten Spiel die Ersetzung durch und geben Sie dann eine Schleife ein, in der die verbleibenden Zeilen einfach so gedruckt werden, wie sie sind :
$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
Schleifentechnik 2, nur für kleinere Dateien : Lesen Sie die gesamte Eingabe in den Speicher und führen Sie eine einzelne Ersetzung durch .
$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
1 1.61803 enthält Beispiele dafür, was mit 1,/re/
, mit und ohne nachfolgende Ereignisse geschieht s//
:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'
Erträge $'1bar\n2bar'
; Das heißt, beide Zeilen wurden aktualisiert, da die Zeilennummer 1
mit der ersten Zeile übereinstimmt und Regex /foo/
- das Ende des Bereichs - erst ab der nächsten Zeile gesucht wird. Daher werden in diesem Fall beide Zeilen ausgewählt und die s/foo/bar/
Substitution wird für beide durchgeführt.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo'
schlägt fehl : mit sed: first RE may not be empty
(BSD / macOS) und sed: -e expression #1, char 0: no previous regular expression
(GNU), da zum Zeitpunkt der Verarbeitung der ersten Zeile (aufgrund der Zeilennummer, 1
die den Bereich beginnt) noch kein regulärer Ausdruck angewendet wurde//
bezieht sich auf nichts.
Mit Ausnahme der sed
speziellen 0,/re/
Syntax von GNU schließt jeder Bereich, der mit einer Zeilennummer beginnt, die Verwendung von effektiv aus //
.