Ich lerne sed. Alles schien gut zu laufen, bis ich auf das N stieß (als nächstes mehrzeilig). Ich habe diese Datei (guide.txt) zum Üben / Verstehen / Kontext erstellt. Hier ist der Inhalt dieser Datei ...
This guide is meant to walk you through a day as a Network
Administrator. By the end, hopefully you will be better
equipped to perform your duties as a Network Administrator
and maybe even enjoy being a Network Administrator that much more.
Network Administrator
Network Administrator
I'm a Network Administrator
Mein Ziel ist es daher, ALLE Instanzen von "Netzwerkadministrator" durch "Systembenutzer" zu ersetzen. Da die erste Instanz von "Netzwerkadministrator" durch eine neue Zeile (\ n) getrennt ist, muss der mehrzeilige nächste Operator (N) die Zeile, die mit "Administrator" beginnt, an die vorherige Zeile anhängen, die mit "Netzwerk \ n" endet. . Kein Problem. Ich möchte aber auch alle anderen einzeiligen "Netzwerkadministrator" -Instanzen abfangen.
Durch meine Forschung habe ich gelernt, dass ich zwei Substitutionsbefehle benötige; eine für die durch Zeilenumbrüche getrennte Zeichenfolge und eine für die anderen. Außerdem passiert ein Jive aufgrund der letzten Zeile, die die Substitutionsübereinstimmung enthält, und der nächsten Zeile mit mehreren Zeilen. Also bastle ich das ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> ' guide.txt
Dies gibt diese Ergebnisse zurück ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a Network Administrator that much more.
System User
Network Administrator
I'm a System User
Ich dachte, dass die einzeilige Ersetzung alle "normalen" Instanzen von "Network Administrator" abfangen und gegen "Systembenutzer" austauschen würde, während die mehrzeilige Anweisung ihre Magie auf die durch neue Zeilen getrennte Instanz ausüben würde, aber wie Sie Ich kann sehen, dass es zurückgegeben wird, was ich für unerwartete Ergebnisse halte.
Nach einigem Fummeln landete ich auf diesem ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> s/Network Administrator/System User/
> ' guide.txt
Und voilà, ich bekomme die gewünschte Ausgabe von ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User
Warum funktioniert das und das ursprüngliche sed-Skript nicht? Ich möchte das wirklich verstehen.
Vielen Dank im Voraus für jede Hilfe.
Antworten:
Während Sie lernen
sed
, werde ich mir die Zeit nehmen, um die Antwort von @ John1024 zu ergänzen:1) Bitte beachten Sie, dass Sie
\n
in der Ersatzzeichenfolge verwenden. Dies funktioniert in GNUsed
, ist jedoch nicht Teil von POSIX, sodass ein Backslash und einn
in vielen anderensed
s\n
eingefügt werden (die Verwendung im Muster ist übrigens portabel).Stattdessen schlage ich Folgendes vor
s/Network\([[:space:]]\)Administrator/System\1User/g
: Das entspricht[[:space:]]
Zeilenumbruch oder Leerzeichen, sodass Sie nicht zweis
Befehle benötigen , sondern diese in einem kombinieren. Wenn Sie es mit umgeben\(...\)
, können Sie im Ersatz darauf verweisen: Das\1
wird durch das ersetzt, was im ersten Paar von übereinstimmte\(\)
.2) Um Muster über zwei Linien richtig abzugleichen, sollten Sie das
N;P;D
Muster kennen:Das
N
ist immer append die nächste Zeile ( mit Ausnahme der letzten Zeile, das ist , warum es „adressierte“ mit$!
(= wenn nicht letzte Zeile, man sollte immer vorausgehen betrachtetN
mit$!
versehentlich zu vermeiden , um das Skript endet) Dann nach dem Austausch der.P
Druckt nur Die erste Zeile im Musterraum und dieD
löscht diese Zeile und beginnt den nächsten Zyklus mit den Resten des Musterraums (ohne die nächste Zeile zu lesen). Dies ist wahrscheinlich das, was Sie ursprünglich beabsichtigt hatten.Denken Sie an dieses Muster, Sie werden es oft brauchen.
3) Ein weiteres nützliches Muster für die mehrzeilige Bearbeitung, insbesondere wenn mehr als zwei Zeilen beteiligt sind: Halten Sie das Sammeln von Speicherplatz, wie ich John vorgeschlagen habe:
Ich wiederhole es, um es zu erklären:
H
Hängt jede Zeile an den Haltebereich an. Da dies zu einer zusätzlichen neuen Zeile vor der ersten Zeile führen würde, muss die erste Zeile verschoben und nicht angehängt werden1h
. Das Folgende$!d
bedeutet "für alle Zeilen außer der letzten den Musterraum löschen und von vorne beginnen". Daher wird der Rest des Skripts nur für die letzte Zeile ausgeführt. Zu diesem Zeitpunkt wird die gesamte Datei im Haltebereich gesammelt (verwenden Sie diese also nicht für sehr große Dateien!) Undg
in den Musterbereich verschoben, sodass Sie alle Ersetzungen auf einmal durchführen können, wie Sie es mit der-z
Option können GNUsed
.Dies ist ein weiteres nützliches Muster, das ich beachten sollte.
quelle
Beachten Sie zunächst, dass Ihre Lösung nicht wirklich funktioniert. Betrachten Sie diese Testdatei:
Führen Sie dann den folgenden Befehl aus:
Das Problem ist, dass der Code den letzten nicht ersetzt
Network\nAdministrator
.Diese Lösung funktioniert:
Wir können dies auch auf Ihre anwenden
guide.txt
:Der Schlüssel ist, weiter in Zeilen zu lesen, bis Sie eine finden, die nicht mit endet
Network
. Wenn dies erreicht ist, können die Substitutionen durchgeführt werden.Kompatibilitätshinweis: Alle oben genannten werden
\n
im Ersatztext verwendet. Dies erfordert GNU sed. Es wird nicht auf BSD / OSX sed funktionieren.[Hutspitze zu Philippos .]
Mehrzeilige Version
Wenn dies zur Verdeutlichung beiträgt, finden Sie hier denselben Befehl, der auf mehrere Zeilen verteilt ist:
Wie es funktioniert
:a
Dadurch wird ein Etikett erstellt
a
./Network$/{ $!{N;ba} }
Wenn diese Zeile mit endet
Network
, lesen und hängen Sie die nächste Zeile ( ) an und verzweigen Sie zurück zu label ( ) , wenn dies nicht die letzte Zeile ( ) ist.$!
N
a
ba
s/Network\nAdministrator/System\nUser/g
Nehmen Sie die Ersetzung durch die Zwischen-Newline vor.
s/Network Administrator/System User/g
Nehmen Sie die Ersetzung mit dem Zwischenrohling vor.
Einfachere Lösung (nur GNU)
Mit GNU sed ( nicht BSD / OSX) benötigen wir nur einen Ersatzbefehl:
Und in der
guide.txt
Akte:In diesem Fall
-z
weist sed an, bis zum ersten NUL-Zeichen einzulesen. Da Textdateien niemals ein Nullzeichen haben, wird die gesamte Datei auf einmal eingelesen. Wir können dann die Ersetzung vornehmen, ohne uns Sorgen machen zu müssen, dass eine Zeile fehlt.Diese Methode ist nicht gut, wenn die Datei sehr groß ist (was normalerweise Gigabyte bedeutet). Wenn es so groß ist, kann das gleichzeitige Einlesen den System-RAM belasten.
Lösung, die sowohl auf GNU als auch auf BSD sed funktioniert
Wie von Phillipos vorgeschlagen , ist Folgendes eine tragbare Lösung:
quelle
Network Administrator
zwischen der ersten und der zweiten Zeile dieses Paares aufgeteilt wird, führt Ihre Lösung die Substitution erfolgreich durch. Anschließend werden diese beiden Zeilen gedruckt und das nächste Paar eingelesen. Wenn jedoch die zweite Zeile des ersten Paares mit endetNetwork
und die erste Zeile des zweiten Paares mit beginntAdministrator
, fehlt der Code. Mein Code vermeidet dies, indem er Zeilen einliest, bis er eine findet, die nicht mit endetNetwork
.sed
: Die\n
im Ersatz ist nicht im Standard definiert.sed 'H;1h;$!d;x;s/Network\([[:space:]]\)Administrator/System\1User/g'
ist eine tragbare Möglichkeit, dies zu tun.