0

I am using sed to replace an attribute in an XML file and up until now it was working just fine.

I have an XML file that contains a tag like this:

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

Whenever a new EBA file is deployed, the location attribute needs to be replaced. For example: (different build timestamp)

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

I was doing this by using a sed expression with labels:

: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"

It was working perfectly, until I came across another situation when I actually have other tags under <osgiApplication>. For example:

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

<anotherTag />

When this happens, due to the .*\/> criterion in the sed expression, everything is replaced until the very last />. I only want the first one occurrence to be replaced.

In other words, if I have:

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

I want the replacement to be:

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

But what I'm currently getting is:

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

I've been searching around for a solution but found none :( Any suggestions will be really appreciated. Thanks

  • 2
    Use something like xmlstarlet and xpath. Not sed. This is why. – roaima Apr 9 '16 at 21:48
  • It was working perfectly ? No offence but your sed command doesn't make sense. It's a weird combination - half range: 0, and half unterminated s command: s|...|... – don_crissti Apr 9 '16 at 21:49
  • If you manage to fix the things @don_crissti pointed out you can try to change the loop that loads all lines into the pattern space to only load until the end of the tag. So instead of :a;N;$!ba; you could try :a;N;/\/>/!ba;. – Lucas Apr 9 '16 at 22:06
2

The best answer to your question really is the canonical, don't use sed. Unless the problem is line-oriented and can be expressed as a RE, sed is the wrong tool. Yours is such a case: because XML is not a regular language, any RE will fail sooner or later, given enough complexity. The only bullet-proof solution is to use an XML parser such as the SAX parser that comes in the Python standard library.

Howsomever, one good hack deserves another. If you want to get your script working again while you investigate SAX parsers, you could try two things: a more restrictive RE, or awk.

A more restrictive RE could be ([^/>]+) instead of .*. You could be burned by a filename ending in >, but you could guard against that and my guess is the producer won't create such a filename anyway. Pro tip: when you're looking for a fencepost with .*, use a negative character class instead.

A better choice would be awk, something like

# 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 = ""
}

This sets APP when it finds a suitable opening tag, and replaces it with the string named by REPLACEMENT, which you provide. You could take a little more care, and reset APP with a warning if you encounter /[/]> *$/ while it's set. But we're still just hacking, because nothing about XML says anything about tags starting and stopping at the beginning/ending of lines.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.