XMLStarlet - replace a part of attribute

1.5k views Asked by At

I am using XMLStartlet for a quick deploy cmd (Windows) script for my app, and I am changing the configuration xml file.

Manipulation of the whole nodes/attributes works just perfect, but i need to replace a part of an attribute with a specific value, for example:

<list>
    <address id="a1">
        <data url="http://localhost:8000/a1.html" />
    </address>
    <address id="a2">
        <data url="http://localhost:8000/a2.html" />
    </address>
</list>

I need to change the port part of a /list/address/data/@url to get:

<list>
    <address id="a1">
        <data url="http://localhost:8001/a1.html" />
    </address>
    <address id="a2">
        <data url="http://localhost:8001/a2.html" />
    </address>
</list>

Any help with a suitable xmlstarlet command would be much appreciated. I don't want to mix sed into my script.

2

There are 2 answers

0
npostavs On BEST ANSWER

Using XPath string functions, concat and substring-after:

xmlstarlet ed -u /list/address/data/@url ^
  -x "concat('http://localhost:8001/', substring-after(substring-after(., 'http://localhost:'), '/'))" ^
  addr-list.xml > new-addr-list.xml
move new-addr-list.xml addr-list.xml

You can edit --inplace instead of move:

xmlstarlet ed --inplace -u /list/address/data/@url ^
  -x "concat('http://localhost:8001/', substring-after(substring-after(., 'http://localhost:'), '/'))" ^
  addr-list.xml
0
MC ND On

For a batch + xmlstarlet solution

@echo off
    setlocal enableextensions disabledelayedexpansion

    set "count=1"

    rem For each url in the xml file
    for /f "delims=" %%v in ('
        xml sel -t -v "/list/address/data/@url" input.xml
    ') do (

        rem Split the line using colons as delimiters
        rem So we have %%a = http    %%b = //localhost    %%c = 8001/....
        for /f "tokens=1,2,* delims=:" %%a in ("%%v") do (

            rem Remove the port number from %%c using the numbers as delimiters
            for /f "tokens=* delims=0123456789" %%d in ("%%c") do (

                rem Here we have all the needed elements. Retrieve the number of the 
                rem element being updated (with delayed expansion) and update the 
                rem xml document (without delayed expansion to avoid problems)
                setlocal enabledelayedexpansion
                for %%n in (!count!) do ( 
                    endlocal
                    xml edit -L -u "/list/address[%%n]/data/@url" -v "%%a:%%b:8003%%d" input.xml
                )
            )
        )
        rem This instance has been processed. Increment counter
        set /a "count+=1"
    )