“sed” regex help: Como substituir caracteres

0

Eu quero mudar caracteres em um arquivo XML usando sed. A entrada é assim:

<!-- Input -->
<root>
  <tree foo="abcd" bar="abccdcd" />
  <dontTouch foo="asd" bar="abc" />
</root>

Agora, quero alterar todos os c para X na tag de barra do elemento da árvore.

<!-- Output -->
<root>
  <tree foo="abcd" bar="abXXdXd" />
  <dontTouch foo="asd" bar="abc" />
</root>

Como está o comando sed correto? Por favor, considere, pode haver mais de uma ocorrência de c (um ao lado do outro ou não) em uma tag ...

Eu tentei isso sozinho, mas ele não vai alterar vários c, e ele adiciona um X: (

sed -i 's/\(<tree.*bar=\".*\)c\(.*\"\/>\)/X/g' Input.xml

Editar: mais alguns detalhes;)

  • Este é um trabalho único, depois que o documento é alterado, nunca mais vou tocá-lo

  • A estrutura é tão fácil quanto acima. Isso significa que eu posso pegar todas as linhas (isso funciona) com:

    cat input.xml | grep ""

Então, supondo que eu tenha a string correta extraída, e saiba onde escrevê-la após a modificação: Como alterar 'abcdeccd' para 'abXdeXXd'? Este não é realmente um problema XML, mas um regex, ou estou errado aqui?

    
por powerbar 28.03.2012 / 11:26

2 respostas

1

Isso pode funcionar para você (GNU sed?):

sed '/^\s*<tree.*\<bar="/!b;s//&\n/;:a;s/\n\([^c"]\+\)/\n/;ta;s/\nc/X\n/;ta;:b;s/\n//' XML
    
por 02.04.2012 / 23:07
3

Como a RedGrittyBrick disse, a melhor maneira de fazer isso é usar um analisador XML, escolhendo o elemento, traduzindo os caracteres e, em seguida, escrevê-lo de volta usando uma biblioteca XML. Isso não lhe dará surpresas desagradáveis, resistirá ao teste do tempo, etc. Não é apenas o melhor, é muito superior a outras coisas. Outras soluções mais ou menos instantaneamente se tornam pesadelos para depurar, e certamente haverá problemas ocultos mais ou menos em qualquer lugar.

Se é apenas uma tarefa simples que precisa ser feita uma vez, e uma é muito cuidadosa, e uma verifica o resultado, etc., etc., etc., então pode ser menos trabalho fazer isso do jeito ruim. Mas vai surpreendê-lo algum dia se você fizer disso um hábito.

Como exemplo, aqui está uma das formas ruins que parecem funcionar, mas não se baseia apenas no XML válido , mas na sintaxe mais ou menos exata que você descreveu anteriormente, que é apenas um subconjunto de XML válido, e assim XML válido é certamente capaz de fazer o código falhar (e se alguém adicionar um sinal '>' em uma das tags? Adicione um especial E se alguém não usar aspas? Adicione um caso especial e assim por diante). Este é o problema de não usar um parser real. Alguns cuidados foram tomados abaixo para agir como um pseudoparser, pelo menos, lendo a tag, então agindo sobre ela, depois escrevendo de volta, mas existem ferramentas prontas para isso que foram testadas extensivamente.

#!/bin/sh
IFS='\n'
while read i; do
    if $(printf -- "${i}" | grep -qE '<tree [^>]+ bar="[^'"${1}"'"]*'"${1}"); then
        ORIGTAG=$(printf -- "${i}" | sed 's#^.*<tree [^>]\+ bar="\([^"]\+\)".*$##g')
        NEWTAG=$(printf -- "${ORIGTAG}" | tr "${1}" "${2}")
        printf -- "${i}\n" | sed 's#\(^.*<tree [^>]\+ bar="\)'"${ORIGTAG}"'\(".*$\)#'"${NEWTAG}"'#g'
    else
        printf -- "${i}\n"
    fi
done < "${3}"

Uso: script.sh [caractere a substituir] [substituindo caractere] [nome do arquivo], por exemplo

script.sh c X myfile

IFS define o "separador de campo interno" no shell para nova linha, para manter os espaços em branco no início das linhas.

while read lê o arquivo de entrada (dado como argumento 3 para o script) linha por linha.

grep verifica se a tag específica está na linha atual E se a tag contém o caractere a ser traduzido. Se sim, vá para sed logic; se não, retorne a linha como está.

sed seleciona a tag antiga, executa uma tradução de caractere e retorna a linha com a nova tag.

Como você pode ver, ninguém gostaria de encontrar este script e ter que depurá-lo. Se isso é qualquer coisa mais do que um trabalho único, não faça assim. Para a sanidade de futuros observadores.

    
por 28.03.2012 / 12:38

Tags