Utilizzo di sed per trovare e sostituire una stringa complessa (preferibilmente con regex)

Ho un file con i seguenti contenuti:

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

e ho bisogno di creare uno script che cambi il” nome “nella prima riga in” qualcosa “, la” password “nella seconda riga in” qualcosaltro “e il” nome “in la terza riga a “qualcosa di diverso”. Non posso fare affidamento sullordine di queste occorrenze nel file, quindi non posso semplicemente sostituire la prima occorrenza di “nome” con “qualcosa” e la seconda occorrenza di “nome” con “qualcosa di diverso”. In realtà ho bisogno di fare una ricerca per le stringhe circostanti per assicurarmi di trovare e sostituire la cosa corretta.

Finora ho provato questo comando per trovare e sostituire la prima occorrenza “nome”:

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

tuttavia non funziona, quindi penso che alcuni di questi caratteri potrebbero aver bisogno di escape, ecc.

Idealmente, io ” Mi piacerebbe poter usare regex per abbinare solo le due occorrenze “username” e sostituire solo il “nome”. Qualcosa di simile ma con sed:

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

e sostituisci il contenuto tra parentesi con “qualcosa”.

È possibile?

Commenti

  • Nota che praticamente qualsiasi soluzione basata su regexp, a meno che non sia estremamente artificiosa, rischierà interrompendosi ogni volta che il formato di input cambia. Le espressioni regolari sono una cattiva scelta per trattare XML, SGML o derivati (cosa che a me sembra).
  • Approvato! Prendi in considerazione lutilizzo di XQuery, ad esempio: w3schools.com/xquery/default.asp . Questo è lo standard W3C per il recupero e la manipolazione del contenuto XML.

Risposta

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

Questo è, penso, quello che stai cercando.

Spiegazione:

  • le parentesi nella prima parte definiscono gruppi (stringhe in effetti) che possono essere riutilizzati nella seconda parte
  • \1, \2, ecc. nella seconda parte sono riferimenti al i-esimo gruppo acquisito nella prima parte (la numerazione inizia con 1)
  • -E abilita espressioni regolari estese (necessarie per + e raggruppamento).

Commenti

  • +1 per lopzione -E
  • it lascia un file di backup, con il nome (original name) + "-E".
  • Su OSX ottengo ‘ sed: 1: ” s / (< nome utente >. +) nome (. + … “: \ 1 no definito nella RE ‘. Ho incollato lesempio esatto di questa domanda in un file. quindi ho eseguito il comando da questa risposta su quel file. Forse OSX ha una sintassi diversa?
  • La versione gnu di sed supporta il parametro ” -E “, ma non ufficiale. ‘ non è nemmeno menzionato nella manpage. Se vuoi usare la regex estesa, devi usare il parametro ” -r “.
  • @ deweydb Secondo questa risposta , dovresti utilizzare \( e \) invece di ( e ).

Risposta

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/ prima di s dice a sed per lavorare solo su righe che contengono la stringa “username”.

Commenti

  • Elegante, efficiente e perfettamente adatto al caso. +1

Risposta

Se sed non è difficile requisito, meglio utilizzare uno strumento dedicato.

Se il tuo file è XML valido (non solo quei 3 tag che sembrano XML), puoi utilizzare XMLStarlet :

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

Quanto sopra funzionerà anche in situazioni che sarebbero difficili da risolvere con espressioni regolari:

  • Può sostituire i valori dei tag senza specificare i loro valori correnti.
  • Può sostituire i valori anche se sono solo sottoposti a escape e non sono racchiusi in CDATA.
  • Può sostituire i valori anche se i tag hanno attributi.
  • Può facilmente sostituire solo le occorrenze di tag, se ce ne sono più con lo stesso nome.
  • Può formattare lXML modificato indentandolo.

Breve dimostrazione di quanto sopra:

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> 

Risposta

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

Puoi semplicemente utilizzare gli indirizzi a s nel numero che precede “s” che indica il numero di riga.

Anche il numero alla fine dice a sed di sostituire la seconda corrispondenza invece di sostituire la prima incontro.

Risposta

Devi citare \[.*^$/ nella parte dellespressione regolare del s e \&/ nella parte sostitutiva, più le nuove righe. Lespressione regolare è un espressione regolare di base e inoltre devi citare il delimitatore per il comando s.

Puoi scegliere un delimitatore diverso per evitare di dover citare /. Dovrai invece citare quel carattere, ma di solito lo scopo di cambiare il delimitatore è sceglierne uno che non compare né nel testo da sostituire né nel testo sostitutivo.

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

Puoi utilizzare i gruppi per evitare di ripetere alcune parti nel testo sostitutivo e accogliere variazioni su queste parti.

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

Risposta

Per sostituire la parola “nome” con la parola “qualcosa”, utilizza:

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

Questo sostituirà tutte le occorrenze della parola specificata.

Finora tutto viene emesso sullo standard output, puoi usare:

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

per salvare le modifiche in un altro file.

Rispondi

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

quindi per sostituire il valore in un file delle proprietà

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

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *