Sed gebruiken om complexe tekenreeks te zoeken en te vervangen (bij voorkeur met regex)

Ik heb een bestand met de volgende inhoud:

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

en ik moet een script maken dat de” naam “op de eerste regel verandert in” iets “, het” wachtwoord “op de tweede regel in” iets anders “, en de” naam “in de derde regel naar “iets anders”. Ik kan “niet vertrouwen op de volgorde waarin deze voorkomen in het bestand, dus ik kan” niet de eerste keer dat “naam” voorkomt eenvoudig vervangen door “iets” en de tweede keer dat “naam” voorkomt door “iets anders”. Ik moet eigenlijk naar de omringende strings zoeken om er zeker van te zijn dat ik “het juiste vind en vervang.

Tot dusver heb ik dit commando geprobeerd om de eerste” name “-instantie te vinden en te vervangen:

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

het werkt echter niet, dus ik denk dat sommige van deze tekens moeten worden ontsnapt, enz.

Idealiter zou ik ” Ik zou graag regex kunnen gebruiken om de twee “gebruikersnaam” -versies te matchen en alleen de “naam” te vervangen. Zoiets als dit maar met sed:

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

en vervang de inhoud tussen haakjes door “iets”.

Is dit mogelijk?

Reacties

  • Houd er rekening mee dat vrijwel elke op regexp gebaseerde oplossing, tenzij extreem gekunsteld, het risico loopt breken elke keer dat het invoerformaat verandert. Regexps zijn een slechte keuze voor het omgaan met XML, SGML of afgeleiden (wat mij lijkt).
  • Goedgekeurd! Overweeg om XQuery bijvoorbeeld te gebruiken: w3schools.com/xquery/default.asp . Dit is de W3C-standaard voor het ophalen en manipuleren van XML-inhoud.

Answer

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

Dit is, denk ik, waar je naar op zoek bent.

Uitleg:

  • haakjes in het eerste deel definiëren groepen (in feite strings) die kan hergebruikt worden in het tweede deel
  • \1, \2, etc. in het tweede deel zijn verwijzingen naar de i-de groep vastgelegd in het eerste deel (de nummering begint met 1)
  • -E maakt uitgebreide reguliere expressies mogelijk (nodig voor + en groepering).

Reacties

  • +1 voor de -E optie
  • het laat een back-upbestand achter, met de naam (original name) + "-E".
  • Op OSX krijg ik ‘ sed: 1: ” s / (< gebruikersnaam >. +) naam (. + … “: \ 1 niet gedefinieerd in de RE ‘. Ik heb het exacte voorbeeld van deze vraag in een bestand geplakt. toen heb ik de opdracht van dit antwoord op dat bestand uitgevoerd. Misschien heeft OSX een andere syntaxis?
  • De GNU-versie van sed ondersteunt de parameter ” -E “, maar niet officieel. Het ‘ wordt niet eens genoemd in de manpage. Als je de uitgebreide regex wilt gebruiken, moet je de parameter ” -r ” gebruiken.
  • @ deweydb Volgens dit antwoord moet u \( en \) gebruiken in plaats van ( en ).

Antwoord

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

De /username/ voor de s vertelt sed om alleen te werken op regels die de string “gebruikersnaam” bevatten.

Reacties

  • Elegant, efficiënt en perfect passend voor het geval. +1

Antwoord

Als sed niet moeilijk is vereiste, kunt u beter een speciale tool gebruiken.

Als uw bestand geldige XML is (niet alleen die 3 XML-uitziende tags), dan kunt u XMLStarlet gebruiken :

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

Het bovenstaande werkt ook in situaties die moeilijk op te lossen zijn met reguliere expressies:

  • Kan de waarden van de tags vervangen zonder hun huidige waarden op te geven.
  • Kan de waarden vervangen, zelfs als ze alleen zijn voorzien van escapecodes en niet zijn ingesloten in CDATA.
  • Kan de waarden vervangen, zelfs als de tags hebben attributen.
  • Kan gemakkelijk alleen voorkomende tags vervangen als er meerdere met dezelfde naam zijn.
  • Kan de gewijzigde XML opmaken door deze te laten inspringen.

Korte demonstratie van het bovenstaande:

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> 

Antwoord

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

U kunt eenvoudig adressen een s in het nummer voorafgaand aan “s” wat het regelnummer aangeeft.

Ook het nummer aan het einde vertelt sed om de tweede overeenkomst te vervangen in plaats van de eerste bij elkaar passen.

Antwoord

U moet \[.*^$/ citeren in het reguliere-expressiegedeelte van de s commando en \&/ in het vervangende deel, plus nieuwe regels. De reguliere expressie is een reguliere reguliere expressie , en daarnaast moet je het scheidingsteken voor het s commando aanhalen.

U kunt een ander scheidingsteken kiezen om te voorkomen dat u / moet citeren. Je zult in plaats daarvan dat teken moeten citeren, maar meestal is het punt van het wijzigen van het scheidingsteken er een te kiezen die niet voorkomt in de te vervangen tekst of de vervangende tekst.

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

U kunt groepen gebruiken om herhaling van sommige delen in de vervangende tekst te vermijden, en variatie op deze delen mogelijk maken.

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

Antwoord

Gebruik voor het vervangen van het woord “name” door het woord “iets”:

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

Dat gaat alle voorkomens van het gespecificeerde woord vervangen.

Tot dusver is alles uitgevoerd naar standaarduitvoer, je kunt gebruiken:

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

om de wijzigingen in een ander bestand op te slaan.

Antwoord

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

dus om waarde in een eigenschappenbestand te vervangen

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

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *