sed: substitui apenas a primeira ocorrência de curinga

0

Estou usando o sed para substituir um atributo em um arquivo XML e até agora estava funcionando bem.

Eu tenho um arquivo XML que contém uma tag como esta:

<osgiApplication id="com.mycompany.site.app"
    location="com.mycompany.site.app-1.0.0.20160406155451.eba"
    name="com.mycompany.site.app" />

Sempre que um novo arquivo EBA é implantado, o atributo location precisa ser substituído. Por exemplo: (timestamp de compilação diferente)

<osgiApplication id="com.mycompany.site.app"
    location="com.mycompany.site.app-1.0.0.20160410173452.eba"
    name="com.mycompany.site.app" />

Eu estava fazendo isso usando uma expressão sed com rótulos:

:a;N;$!ba;0,s|<osgiApplication id="com.mycompany.site.app".*\/>|<osgiApplication id="com.mycompany.site.app" location="com.mycompany.site.app-1.0.0.20160406155451.eba"

Ele estava funcionando perfeitamente , até que me deparei com outra situação quando na verdade tenho outras tags em <osgiApplication> . Por exemplo:

<osgiApplication id="com.mycompany.site.app"
    location="com.mycompany.site.app-1.0.0.20160406155451.eba"
    name="com.mycompany.site.app" />

<anotherTag />

Quando isso acontece, devido ao critério .*\/> na expressão sed, tudo é substituído até o último /> . Eu só quero que a primeira ocorrência seja substituída.

Em outras palavras, se eu tiver:

<osgiApplication id="com.mycompany.site.app"
    location="com.mycompany.site.app-1.0.0.20160406155451.eba"
    name="com.mycompany.site.app" />
<anotherTag />
<anotherTag />
<anotherTag />

Eu quero que a substituição seja:

<osgiApplication id="com.mycompany.site.app"
    location="com.mycompany.site.app-1.0.0.20160410173452.eba"
    name="com.mycompany.site.app" />
<anotherTag />
<anotherTag />
<anotherTag />

Mas o que estou recebendo atualmente é:

<osgiApplication id="com.mycompany.site.app"
    location="com.mycompany.site.app-1.0.0.20160410173452.eba"
    name="com.mycompany.site.app" />

Eu tenho procurado por uma solução, mas não encontrei nenhuma :( Qualquer sugestão será muito apreciada. Obrigado

    
por Phil 09.04.2016 / 23:40

1 resposta

2

A melhor resposta para sua pergunta é realmente canônica, não use sed . A menos que o problema seja orientado à linha e possa ser expresso como um RE, sed é a ferramenta errada. O seu é um desses casos: como o XML não é uma linguagem regular, qualquer > RE falhará mais cedo ou mais tarde, considerando a complexidade. A única solução à prova de balas é usar um analisador XML, como o analisador SAX, que vem na biblioteca padrão do Python.

Howsomever, um bom hack merece outro. Se você quiser que seu script funcione novamente enquanto investiga os analisadores SAX, tente duas coisas: um RE mais restritivo ou um awk.

Um RE mais restritivo poderia ser ([^/>]+) em vez de .* . Você poderia ser queimado por um nome de arquivo terminando em > , mas você poderia se proteger contra isso e meu palpite é que o produtor não criará um nome de arquivo assim mesmo. Dica profissional: quando você estiver procurando por um fencepost com .* , use uma classe de caracteres negativos.

Uma melhor escolha seria awk, algo como

# use awk -F '["]' to set FS to a double-quote character
/<osgiApplication id=.*app"/ {
    APP=$2
    next
}
APP && /location=/ {
    if (index($2, APP) {
        substr($2, REPLACEMENT, $0)
    }
    APP = ""
}

Isso define o APP quando ele encontra uma tag de abertura adequada e a substitui pela string nomeada por REPLACEMENT, que você fornece. Você poderia tomar um pouco mais de cuidado e redefinir o APP com um aviso se encontrar /[/]> *$/ enquanto estiver definido. Mas ainda estamos apenas hackeando, porque nada em XML diz nada sobre tags começando e parando no começo / fim das linhas.

    
por 10.04.2016 / 00:28

Tags