Tenho um arquivo com o seguinte conteúdo:
<username><![CDATA[name]]></username> <password><![CDATA[password]]></password> <dbname><![CDATA[name]]></dbname>
e eu preciso fazer um script que altere o” nome “na primeira linha para” algo “, a” senha “na segunda linha para” algoelse “e o” nome “em a terceira linha para “algo diferente”. Não posso confiar na ordem dessas ocorrências no arquivo, portanto, não posso simplesmente substituir a primeira ocorrência de “nome” por “algo” e a segunda ocorrência de “nome” por “algo diferente”. Na verdade, preciso fazer uma pesquisa pelas strings ao redor para ter certeza de que estou encontrando e substituindo a coisa correta.
Até agora, tentei este comando para localizar e substituir a primeira ocorrência de “nome”:
sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml
no entanto, não está funcionando, então estou pensando que alguns desses caracteres podem precisar de escape, etc.
Idealmente, eu ” Adoraria ser capaz de usar regex para apenas combinar as duas ocorrências de “nome de usuário” e substituir apenas o “nome”. Algo assim, mas com sed
:
<username>.+?(name).+?</username>
e substitua o conteúdo dos colchetes por “algo”.
Isso é possível?
Comentários
- Observe que praticamente qualquer solução baseada em regexp, a menos que extremamente planejada, corre o risco quebrando sempre que o formato de entrada muda. Regexps são uma escolha ruim para lidar com XML, SGML ou derivados (o que me parece).
- Aprovado! Considere o uso de XQuery, por exemplo: w3schools.com/xquery/default.asp . Este é o padrão W3C para recuperar e manipular conteúdo XML.
Resposta
sed -i -E "s/(<username>.+)name(.+<\/username>)/\1something\2/" file.xml
Isso é, eu acho, o que você está procurando.
Explicação:
- os parênteses na primeira parte definem grupos (strings na verdade) que podem ser reutilizados na segunda parte
-
\1
,\2
, etc. na segunda parte são referências ao i-ésimo grupo capturado na primeira parte (a numeração começa com 1) -
-E
ativa expressões regulares estendidas (necessárias para+
e agrupamento).
Comentários
- +1 para a opção -E
- it deixa para trás um arquivo de backup, com o nome
(original name) + "-E"
. - No OSX eu obtenho ‘ sed: 1: ” s / (< nome de usuário >. +) nome (. + … “: \ 1 não definido no RE ‘. Colei o exemplo exato desta pergunta em um arquivo. então eu executei o comando desta resposta naquele arquivo. Talvez o OSX tenha uma sintaxe diferente?
- A versão gnu do sed oferece suporte ao parâmetro ” -E “, mas não oficial. Ele ‘ nem mesmo é mencionado na página de manual. Se quiser usar o regex estendido, você deverá usar o parâmetro ” -r “.
- @ deweydb De acordo com esta resposta , você deve usar
\(
e\)
em vez de(
e)
.
Resposta
sed -e "/username/s/CDATA\[name\]/CDATA\[something\]/" \ -e "/password/s/CDATA\[password\]/CDATA\[somethingelse\]/" \ -e "/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/" file.txt
O /username/
antes do s
diz ao sed para funcionar apenas em linhas que contenham a string “nome de usuário”.
Comentários
- Elegante, eficiente e perfeitamente adequado para o caso. +1
Resposta
Se sed
não for difícil requisito, é melhor usar uma ferramenta dedicada.
Se o seu arquivo for XML válido (não apenas aquelas 3 tags que parecem XML), então você pode usar XMLStarlet :
xml ed -P -O -L \ -u "//username/text()" -v "something" \ -u "//password/text()" -v "somethingelse" \ -u "//dbname/text()" -v "somethingdifferent" file.xml
O acima também funcionará em situações que seriam difíceis de resolver com expressões regulares:
- Pode substituir os valores das tags sem especificar seus valores atuais.
- Pode substituir os valores mesmo se eles tiverem apenas escape e não estiverem incluídos em CDATA.
- Pode substituir os valores mesmo se as tags têm atributos.
- Pode facilmente substituir apenas as ocorrências de tags, se houver vários com o mesmo nome.
- Pode formatar o XML modificado recuando-o.
Breve demonstração do acima:
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>
Resposta
$ sed -e "1s/name/something/2" \ -e "3s/name/somethingdifferent/2" \ -e "s/password/somethingelse/2" sample.xml
Você pode simplesmente usar endereços de s no número anterior a “s” que indica o número da linha.
Além disso, o número no final diz a sed
para substituir a segunda correspondência em vez de substituir a primeira Combine.
Resposta
Você precisa citar \[.*^$/
na parte da expressão regular do s
comando e \&/
na peça de substituição, mais novas linhas. A expressão regular é uma expressão regular básica e, além disso, você precisa citar o delimitador para o comando s
.
Você pode escolher um delimitador diferente para evitar a necessidade de citar /
. Você terá que citar esse caractere, mas normalmente o objetivo de alterar o delimitador é escolher um que não ocorra no texto a ser substituído ou no texto de substituição.
sed -e "s~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~"
Você pode usar grupos para evitar a repetição de algumas partes no texto de substituição e acomodar variações nessas partes.
sed -e "s~\(<username><!\[[A-Z]*\[\)name\(\]\]></username>\)~\1something\2~" sed -e "s~\(<username>.*[^A-Za-z]\[\)name\([^A-Za-z].*</username>\)~\1something\2~"
Resposta
Para substituir a palavra “nome” pela palavra “algo”, use:
sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml
Isso vai substituir todas as ocorrências da palavra especificada.
Até agora tudo é enviado para a saída padrão, você pode usar:
sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml > anotherfile.xml
para salvar as alterações em outro arquivo.
Resposta
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]... -r, --regexp-extended use extended regular expressions in the script.
para substituir o valor em um arquivo de propriedades
sed -i -r "s/MAIL\=(.+)/MAIL\[email protected]/" etc/service.properties