Jag har en fil med följande innehåll:
<username><![CDATA[name]]></username> <password><![CDATA[password]]></password> <dbname><![CDATA[name]]></dbname>
och jag måste skapa ett skript som ändrar” namnet ”i första raden till” något ”,” lösenordet ”på den andra raden till” somethingelse ”och” namnet ”i tredje raden till ”något annorlunda”. Jag kan inte lita på ordningen på dessa som förekommer i filen, så jag kan inte helt enkelt ersätta den första förekomsten av ”namn” med ”något” och den andra förekomsten av ”namn” med ”något annorlunda”. Jag måste faktiskt söka efter de omgivande strängarna för att se till att jag hittar och ersätter rätt sak.
Hittills har jag försökt det här kommandot för att hitta och ersätta den första ”namn” -händelsen:
sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml
men det fungerar inte så jag tänker att några av dessa karaktärer kan behöva fly, etc.
Helst, jag ” Jag älskar att kunna använda regex för att bara matcha de två ”användarnamn” -händelserna och bara ersätta ”namnet”. Något liknande men med sed
:
<username>.+?(name).+?</username>
och ersätt innehållet inom parenteserna med ”något”.
Är detta möjligt?
Kommentarer
- Observera att i stort sett alla regexp-baserade lösningar, om inte extremt konstruerade, riskerar bryter när som helst ingångsformatet ändras. Regexps är ett dåligt val för att hantera XML, SGML eller derivat (vilket detta ser ut för mig).
- Godkänt! Överväg att använda XQuery till exempel: w3schools.com/xquery/default.asp . Detta är W3C-standarden för att hämta och manipulera XML-innehåll.
Svar
sed -i -E "s/(<username>.+)name(.+<\/username>)/\1something\2/" file.xml
Det här är, tror jag, det du letar efter.
Förklaring:
- parenteser i första delen definierar grupper (faktiskt strängar) som kan återanvändas i andra delen
-
\1
,\2
, etc. i andra delen är hänvisningar till i-grupp som fångats i första delen (numreringen börjar med 1) -
-E
möjliggör utökade reguljära uttryck (behövs för+
och gruppering).
Kommentarer
- +1 för -E-alternativet
- it lämnar en reservfil med namnet
(original name) + "-E"
. - På OSX får jag ’ sed: 1: ” s / (< användarnamn >. +) namn (. + … ”: \ 1 inte definierad i RE ’. Jag klistrade in det exakta exemplet från den här frågan i en fil. sedan körde jag kommandot från det här svaret på den filen. OSX har kanske annan syntax?
- GNU-versionen av sed stöder parametern ” -E ”, men inte officiell. Det ’ nämns inte ens på manpage. Om du vill använda den utökade regexen måste du istället använda parametern ” -r ”.
- @ deweydb Enligt detta svar ska du använda
\(
och\)
istället för(
och)
.
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öre s
säger till sed att bara arbeta på rader som innehåller strängen ”användarnamn”.
Kommentarer
- Elegant, effektivt och perfekt anpassat för ärendet. +1
Svar
Om sed
inte är svårt krav, använd bättre ett dedikerat verktyg istället.
Om din fil är giltig XML (inte bara de tre XML-taggarna) kan du använda XMLStarlet :
xml ed -P -O -L \ -u "//username/text()" -v "something" \ -u "//password/text()" -v "somethingelse" \ -u "//dbname/text()" -v "somethingdifferent" file.xml
Ovanstående fungerar också i situationer som skulle vara svåra att lösa med reguljära uttryck:
- Kan ersätta värdena för taggarna utan att ange deras aktuella värden.
- Kan ersätta värdena även om de bara är undantagna och inte finns i CDATA.
- Kan ersätta värden även om taggarna har attribut.
- Kan enkelt ersätta bara förekomster av taggar, om det finns flera med samma namn.
- Kan formatera den modifierade XML genom att dra den.
Kort demonstration av ovanstå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 helt enkelt använda adresserna a s i siffran före ”s” som anger radnumret.
Siffran till slut säger också till sed
att ersätta den andra matchen istället för att ersätta den första match.
Svar
Du måste citera \[.*^$/
i det reguljära uttrycksdelen av s
-kommando och \&/
i ersättningsdelen, plus nya rader. Det reguljära uttrycket är ett grundläggande reguljärt uttryck , och dessutom måste du citera avgränsaren för kommandot s
.
Du kan välja en annan avgränsare för att undvika att citera /
. Du måste citera den karaktären istället, men vanligtvis ändras avgränsaren genom att välja en som inte finns i antingen texten som ska ersättas eller ersättningstexten.
sed -e "s~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~"
Du kan använda grupper för att undvika att upprepa vissa delar i ersättningstexten och anpassa variationer på dessa delar.
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
För att ersätta ”namn” -ordet med ”något” -ordet, använd:
sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml
Det kommer att ersätta alla förekomster av det angivna ordet.
Hittills har allt matats ut till standardutdata, du kan använda:
sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml > anotherfile.xml
för att spara ändringarna till en annan fil.
Svar
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]... -r, --regexp-extended use extended regular expressions in the script.
så att ersätta värde i en egenskapsfil
sed -i -r "s/MAIL\=(.+)/MAIL\[email protected]/" etc/service.properties