Verwenden von sed zum Suchen und Ersetzen komplexer Zeichenfolgen (vorzugsweise durch Regex)

Ich habe eine Datei mit dem folgenden Inhalt:

<username><![CDATA[name]]></username> <password><![CDATA[password]]></password> <dbname><![CDATA[name]]></dbname> 

und ich muss ein Skript erstellen, das den“ Namen „in der ersten Zeile in“ etwas „, das“ Passwort „in der zweiten Zeile in“ etwas anderes „und den“ Namen „in ändert die dritte Zeile zu „etwas anderes“. Ich kann mich nicht auf die Reihenfolge verlassen, in der diese in der Datei vorkommen, also kann ich das erste Vorkommen von „name“ nicht einfach durch „etwas“ und das zweite Vorkommen von „name“ durch „etwas anderes“ ersetzen. Ich muss tatsächlich nach den umgebenden Zeichenfolgen suchen, um sicherzustellen, dass ich das richtige Ding finde und ersetze.

Bisher habe ich diesen Befehl versucht, das erste „Name“ -Vorkommen zu finden und zu ersetzen:

sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml 

aber es funktioniert nicht, daher denke ich, dass einige dieser Zeichen möglicherweise entkommen müssen usw.

Idealerweise habe ich “ Ich liebe es, Regex verwenden zu können, um nur die beiden Vorkommen „Benutzername“ abzugleichen und nur den „Namen“ zu ersetzen. So etwas aber mit sed:

<username>.+?(name).+?</username> 

und ersetzen Sie den Inhalt in den Klammern durch „etwas“.

Ist dies möglich?

Kommentare

  • Beachten Sie nur, dass so ziemlich jede auf Regexp basierende Lösung, sofern sie nicht extrem erfunden ist, ein Risiko birgt Unterbrechen jedes Mal, wenn sich das Eingabeformat ändert. Regexps sind eine schlechte Wahl für den Umgang mit XML, SGML oder Derivaten (was mir so erscheint).
  • Genehmigt! Verwenden Sie beispielsweise XQuery: w3schools.com/xquery/default.asp . Dies ist der W3C-Standard zum Abrufen und Bearbeiten von XML-Inhalten.

Antwort

sed -i -E "s/(<username>.+)name(.+<\/username>)/\1something\2/" file.xml 

Dies ist, glaube ich, das, wonach Sie suchen.

Erläuterung:

  • Klammern im ersten Teil definieren Gruppen (tatsächlich Zeichenfolgen), die kann im zweiten Teil wiederverwendet werden
  • \1, \2 usw. im zweiten Teil sind Verweise auf die i-te Gruppe, die im ersten Teil erfasst wurde (die Nummerierung beginnt mit 1)
  • -E aktiviert erweiterte reguläre Ausdrücke (erforderlich für + und Gruppierung).

Kommentare

  • +1 für die Option -E
  • hinterlässt eine Sicherungsdatei mit dem Namen (original name) + "-E".
  • Unter OSX erhalte ich ‚ sed: 1: “ s / (< Benutzername >. +) name (. + … „: \ 1 nicht definiert in der RE ‚. Ich habe das genaue Beispiel aus dieser Frage in eine Datei eingefügt. dann habe ich den Befehl von dieser Antwort auf diese Datei ausgeführt. Vielleicht hat OSX eine andere Syntax?
  • Die gnu-Version von sed unterstützt den Parameter “ -E „, jedoch nicht offiziell. Es ‚ wird in der Manpage nicht einmal erwähnt. Wenn Sie den erweiterten regulären Ausdruck verwenden möchten, müssen Sie stattdessen den Parameter “ -r “ verwenden.
  • @ deweydb Gemäß dieser Antwort sollten Sie \( und \) verwenden anstelle von ( und ).

Antwort

sed -e "/username/s/CDATA\[name\]/CDATA\[something\]/" \ -e "/password/s/CDATA\[password\]/CDATA\[somethingelse\]/" \ -e "/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/" file.txt 

Die /username/ vor der s teilt sed mit um nur mit Zeilen zu arbeiten, die die Zeichenfolge „Benutzername“ enthalten.

Kommentare

  • Elegant, effizient und perfekt für den Fall geeignet. +1

Antwort

Wenn sed nicht schwer ist Verwenden Sie stattdessen besser ein dediziertes Tool.

Wenn Ihre Datei gültiges XML ist (nicht nur diese 3 XML-Tags), können Sie XMLStarlet verwenden :

xml ed -P -O -L \ -u "//username/text()" -v "something" \ -u "//password/text()" -v "somethingelse" \ -u "//dbname/text()" -v "somethingdifferent" file.xml 

Das Obige funktioniert auch in Situationen, die mit regulären Ausdrücken schwer zu lösen wären:

  • Kann die Werte der Tags ersetzen, ohne ihre aktuellen Werte anzugeben.
  • Kann die Werte ersetzen, auch wenn sie nur maskiert und nicht in CDATA eingeschlossen sind.
  • Kann die Werte ersetzen, auch wenn Die Tags haben Attribute.
  • Kann leicht nur das Auftreten von Tags ersetzen, wenn mehrere mit demselben Namen vorhanden sind.
  • Kann das geänderte XML durch Einrücken formatieren.

Kurze Demonstration des oben Gesagten:

bash-4.2$ cat file.xml <sith> <master> <username><![CDATA[name]]></username> </master> <apprentice> <username><![CDATA[name]]></username> <password>password</password> <dbname foo="bar"><![CDATA[name]]></dbname> </apprentice> </sith> bash-4.2$ xml ed -O -u "//apprentice/username/text()" -v "something" -u "//password/text()" -v "somethingelse" -u "//dbname/text()" -v "somethingdifferent" file.xml <sith> <master> <username><![CDATA[name]]></username> </master> <apprentice> <username><![CDATA[something]]></username> <password>somethingelse</password> <dbname foo="bar"><![CDATA[somethingdifferent]]></dbname> </apprentice> </sith> 

Antwort

$ sed -e "1s/name/something/2" \ -e "3s/name/somethingdifferent/2" \ -e "s/password/somethingelse/2" sample.xml 

Sie können einfach die Adressen a verwenden s in der Zahl vor „s“, die die Zeilennummer angibt.

Auch die Zahl am Ende weist sed an, die zweite Übereinstimmung zu ersetzen, anstatt die erste zu ersetzen Spiel.

Antwort

Sie müssen \[.*^$/ im regulären Ausdruck des s Befehl und \&/ im Ersatzteil plus Zeilenumbrüche. Der reguläre Ausdruck ist ein grundlegender regulärer Ausdruck . Außerdem müssen Sie das Trennzeichen für den Befehl s angeben.

Sie können ein anderes Trennzeichen auswählen, um zu vermeiden, dass Sie / angeben müssen. Sie müssen stattdessen dieses Zeichen zitieren, aber normalerweise müssen Sie das Trennzeichen ändern, indem Sie eines auswählen, das weder im zu ersetzenden Text noch im Ersetzungstext vorkommt.

sed -e "s~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~" 

Sie können Gruppen verwenden, um zu vermeiden, dass einige Teile im Ersatztext wiederholt werden, und Variationen dieser Teile berücksichtigen.

sed -e "s~\(<username><!\[[A-Z]*\[\)name\(\]\]></username>\)~\1something\2~" sed -e "s~\(<username>.*[^A-Za-z]\[\)name\([^A-Za-z].*</username>\)~\1something\2~" 

Antwort

Um das Wort „name“ durch das Wort „Something“ zu ersetzen, verwenden Sie:

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml 

Damit werden alle Vorkommen des angegebenen Wortes ersetzt.

Bisher wird alles in Standardausgabe ausgegeben. Sie können Folgendes verwenden:

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml > anotherfile.xml 

, um die Änderungen in einer anderen Datei zu speichern.

Antwort

Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]... -r, --regexp-extended use extended regular expressions in the script. 

um den Wert in einer Eigenschaftendatei zu ersetzen

sed -i -r "s/MAIL\=(.+)/MAIL\[email protected]/" etc/service.properties 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.