Brug af sed til at finde og erstatte kompleks streng (helst med regex)

Jeg har en fil med følgende indhold:

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

og jeg er nødt til at lave et script, der ændrer” navnet “i den første linje til” noget “,” adgangskoden “på den anden linje til” somethingelse “og” navnet “i den tredje linje til “noget andet”. Jeg kan ikke stole på rækkefølgen af disse, der forekommer i filen, så jeg kan ikke bare erstatte den første forekomst af “navn” med “noget” og den anden forekomst af “navn” med “noget andet”. Jeg er faktisk nødt til at søge efter de omkringliggende strenge for at sikre, at jeg finder og erstatter den rigtige ting.

Indtil videre har jeg prøvet denne kommando for at finde og erstatte den første “navn” -forekomst:

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

men det fungerer ikke, så jeg tænker, at nogle af disse tegn muligvis skal flygte osv.

Ideelt set, jeg ” Jeg elsker at kunne bruge regex til bare at matche de to “brugernavn” -forekomster og kun erstatte “navnet”. Noget som dette men med sed:

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

og udskift indholdet i parenteserne med “noget”.

Er dette muligt?

Kommentarer

  • Bare bemærk, at stort set enhver regexp-baseret løsning, medmindre den er ekstremt konstrueret, vil risikere bryde når som helst inputformatet ændres. Regexps er et dårligt valg til at håndtere XML, SGML eller derivater (som dette ser ud til mig).
  • Godkendt! Overvej at bruge XQuery for eksempel: w3schools.com/xquery/default.asp . Dette er W3C-standarden til at hente og manipulere XML-indhold.

Svar

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

Dette er, tror jeg, det du leder efter.

Forklaring:

  • parenteser i første del definerer grupper (faktisk strenge) kan genbruges i anden del
  • \1, \2 osv. i anden del er henvisninger til i-th-gruppe fanget i første del (nummereringen starter med 1)
  • -E muliggør udvidede regulære udtryk (nødvendigt til + og gruppering).

Kommentarer

  • +1 for indstillingen -E
  • it efterlader en sikkerhedskopifil med navnet (original name) + "-E".
  • På OSX får jeg ‘ sed: 1: ” s / (< brugernavn >. +) navn (. + … “: \ 1 ikke defineret i RE ‘. Jeg indsatte det nøjagtige eksempel fra dette spørgsmål i en fil. så kørte jeg kommandoen fra dette svar på den fil. OSX har måske anden syntaks?
  • GNU-versionen af sed understøtter parameteren ” -E “, men ikke officiel. Det ‘ nævnes ikke engang på manpage. Hvis du vil bruge den udvidede regex, skal du i stedet bruge ” -r “.
  • @ deweydb Ifølge dette svar skal du bruge \( 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 fortæller sed for kun at arbejde på linjer, der indeholder strengen “brugernavn”.

Kommentarer

  • Elegant, effektiv og perfekt tilpasset sagen. +1

Svar

Hvis sed ikke er svært krav, brug bedre et dedikeret værktøj i stedet.

Hvis din fil er gyldig XML (ikke kun de 3 XML-udseende tags), kan du bruge XMLStarlet :

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

Ovenstående fungerer også i situationer, der ville være vanskelige at løse med regulære udtryk:

  • Kan erstatte værdierne for tags uden at angive deres aktuelle værdier.
  • Kan erstatte værdierne, selvom de lige er undsluppet og ikke er indeholdt i CDATA.
  • Kan erstatte værdierne, selvom tags har attributter.
  • Kan let erstatte bare forekomster af tags, hvis der er flere med samme navn.
  • Kan formatere den ændrede XML ved at indrykke den.

Kort demonstration af ovenstående:

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 bare bruge adresser a s i nummeret forud for “s”, som angiver linienummeret.

Også tallet til sidst fortæller sed at erstatte den anden kamp i stedet for at erstatte den første match.

Svar

Du skal citere \[.*^$/ i den regulære udtryksdel af s -kommando og \&/ i erstatningsdelen plus nye linjer. Det regulære udtryk er et grundlæggende regulært udtryk , og derudover skal du citere afgrænseren for kommandoen s.

Du kan vælge en anden afgrænser for at undgå at skulle citere /. Du bliver i stedet nødt til at citere dette tegn, men normalt er det at ændre afgrænsningen at vælge en, der ikke forekommer i hverken teksten, der skal erstattes, eller erstatningsteksten.

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

Du kan bruge grupper til at undgå at gentage nogle dele i erstatningsteksten og rumme variation på disse dele.

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 at erstatte ordet “navn” med ordet “noget” skal du bruge:

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

Det vil erstatte alle forekomster af det angivne ord.

Indtil videre er alt sendt til standardoutput, du kan bruge:

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

for at gemme ændringerne i en anden fil.

Svar

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

for at erstatte værdi i en egenskabsfil

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

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *