Ved hjelp av sed for å finne og erstatte kompleks streng (helst med regex)

Jeg har en fil med følgende innhold:

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

og jeg trenger å lage et skript som endrer» navnet «i første linje til» noe «,» passordet «på den andre linjen til» somethingelse «, og» navnet «i tredje linje til «noe forskjellig». Jeg kan ikke stole på rekkefølgen på disse som forekommer i filen, så jeg kan ikke bare erstatte den første forekomsten av «navn» med «noe» og den andre forekomsten av «navn» med «noe forskjellig». Jeg må faktisk søke etter de omkringliggende strengene for å være sikker på at jeg finner og erstatter den riktige tingen.

Så langt har jeg prøvd denne kommandoen for å finne og erstatte den første «navn» -forekomsten:

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

men det fungerer ikke, så jeg tenker at noen av disse tegnene kanskje trenger å rømme osv.

Ideelt sett, jeg » Jeg elsker å kunne bruke regex til å bare matche de to «brukernavn» -forekomster og erstatte bare «navnet». Noe som dette men med sed:

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

og erstatt innholdet i parentes med «noe».

Er dette mulig?

Kommentarer

  • Bare merk at stort sett enhver regexp-basert løsning, med mindre den er ekstremt konstruert, vil risikere bryter når som helst inngangsformatet endres. Regexps er et dårlig valg for å håndtere XML, SGML eller derivater (som dette ser ut for meg).
  • Godkjent! Vurder å bruke XQuery for eksempel: w3schools.com/xquery/default.asp . Dette er W3C-standarden for å hente og manipulere XML-innhold.

Svar

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

Dette er, tror jeg, det du leter etter.

Forklaring:

  • parenteser i første del definerer grupper (faktisk strenger) som kan brukes på nytt i andre del
  • \1, \2, etc. i den andre delen er referanser til i-gruppe fanget i første del (nummereringen starter med 1)
  • -E muliggjør utvidede regulære uttrykk (nødvendig for + og gruppering).

Kommentarer

  • +1 for alternativet -E
  • it etterlater en sikkerhetskopifil, med navnet (original name) + "-E".
  • På OSX får jeg ‘ sed: 1: » s / (< brukernavn >. +) navn (. + … «: \ 1 ikke definert i RE ‘. Jeg limte det eksakte eksemplet fra dette spørsmålet inn i en fil. så kjørte jeg kommandoen fra dette svaret på den filen. Kanskje OSX har annen syntaks?
  • GNU-versjonen av sed støtter » -E » -parameteren, men ikke offisielt. Det ‘ er ikke engang nevnt på hjemmesiden. Hvis du vil bruke den utvidede regexen, må du i stedet bruke parameteren » -r «.
  • @ deweydb Ifølge dette svaret , bør du bruke \( og \) i stedet for ( og ).

Svar

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

/username/ før s forteller sed å bare jobbe på linjer som inneholder strengen «brukernavn».

Kommentarer

  • Elegant, effektiv og perfekt tilpasset saken. +1

Svar

Hvis sed ikke er vanskelig krav, bruk bedre et dedikert verktøy i stedet.

Hvis filen din er gyldig XML (ikke bare de tre XML-kodene), kan du bruke XMLStarlet :

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

Ovennevnte fungerer også i situasjoner som det ville være vanskelig å løse med regulære uttrykk:

  • Kan erstatte verdiene til kodene uten å spesifisere gjeldende verdier.
  • Kan erstatte verdiene selv om de bare er unnslapp og ikke inngår i CDATA.
  • Kan erstatte verdiene selv om kodene har attributter.
  • Kan enkelt erstatte bare forekomster av tagger, hvis det er flere med samme navn.
  • Kan formatere den modifiserte XML ved å innrykke den.

Kort demonstrasjon av det ovennevnte:

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> 

Svar

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

Du kan ganske enkelt bruke adresser a s i tallet foran «s» som indikerer linjenummeret.

Også tallet til slutt forteller sed å erstatte den andre kampen i stedet for å erstatte den første kamp.

Svar

Du må sitere \[.*^$/ i det regulære uttrykksdelen av s kommando og \&/ i erstatningsdelen, pluss nye linjer. Regulært uttrykk er et grunnleggende regulært uttrykk , og i tillegg må du sitere avgrenseren for s -kommandoen.

Du kan velge en annen skilletegn for å unngå å sitere /. Du må sitere dette tegnet i stedet, men vanligvis er poenget med å endre skilletegn å velge en som ikke forekommer i verken teksten som skal erstattes eller erstatningsteksten.

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

Du kan bruke grupper for å unngå å gjenta noen deler i erstatningsteksten, og imøtekomme variasjoner på disse delene.

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

Svar

For å erstatte «navn» -ordet med «noe» -ordet, bruk:

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

Det kommer til å erstatte alle forekomster av det angitte ordet.

Så langt er alt lagt ut til standard utdata, du kan bruke:

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

for å lagre endringene i en annen fil.

Svar

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

for å erstatte verdien i en eiendomsfil

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

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *