it-swarm.com.de

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

Ich habe eine Datei mit folgendem 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 der dritten Zeile in "etwas anderes" ändert. Ich kann mich nicht auf die Reihenfolge verlassen, in der diese in der Datei vorkommen, daher 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 finde und ersetze.

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

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

da es jedoch nicht funktioniert, denke ich, dass einige dieser Charaktere möglicherweise entkommen müssen usw.

Im Idealfall würde ich gerne in der Lage sein, Regex zu verwenden, 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 das möglich?

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

Ich denke, das ist es, wonach Sie suchen.

Erläuterung:

  • klammern im ersten Teil definieren Gruppen (tatsächlich Zeichenfolgen), die im zweiten Teil wiederverwendet werden können
  • \1, \2 usw. im zweiten Teil sind Verweise auf die im ersten Teil erfasste i-te Gruppe (die Nummerierung beginnt mit 1).
  • -E aktiviert erweiterte reguläre Ausdrücke (benötigt für + und Gruppierung).
187
lgeorget
sed -e '/username/s/CDATA\[name\]/CDATA\[something\]/' \
-e '/password/s/CDATA\[password\]/CDATA\[somethingelse\]/' \
-e '/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/' file.txt

Das /username/ vor dem s weist sed an, nur an Zeilen zu arbeiten, die die Zeichenfolge 'Benutzername' enthalten.

16
evilsoup

Wenn sed keine schwierige Anforderung ist, verwenden Sie stattdessen 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 die aktuellen Werte anzugeben.
  • Kann die Werte ersetzen, auch wenn sie nur maskiert und nicht in CDATA enthalten sind.
  • Kann die Werte ersetzen, auch wenn die Tags Attribute haben.
  • Kann einfach 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 genannten:

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>
7
manatwork
$ sed -e '1s/name/something/2' \
      -e '3s/name/somethingdifferent/2' \
      -e 's/password/somethingelse/2' sample.xml

Sie können einfach Adressen wie in der Nummer vor "s" verwenden, die die Zeilennummer angibt.

Außerdem sagt die Zahl am Ende sed, dass die zweite Übereinstimmung ersetzt werden soll, anstatt die erste Übereinstimmung zu ersetzen.

5
A. Wench

Sie müssen \[.*^$/ Im regulären Ausdruck des Befehls s und \&/ Im Ersatzteil sowie Zeilenumbrüche angeben. Der reguläre Ausdruck ist ein regulärer Basisausdruck , und 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~'

Verwenden Sie: Ersetzen Sie das Wort "Name" durch das Wort "Etwas":

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

Das wird alle Vorkommen des angegebenen Wortes ersetzen.

Bisher wird alles auf 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.

1
slackmart
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...

    -r, --regexp-extended
             use extended regular expressions in the script.

so ersetzen Sie den Wert in einer Eigenschaftendatei

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