Replace dynamic content in XML file

22,003

Solution 1

You can use xmlstarlet to edit a XML file in a shell like this :

xmlstarlet edit -L -u "/scs-shop/price[@type='double']" -v '99.66' file.xml

NOTE

  • "/scs-shop/price[@type='double']" is a Xpath expression
  • see xmlstarlet ed --help

Solution 2

The XML way is cool, but if you need to use normal bash tools, you can modify a line using sed. For instance:

PRICE=123
sed -i "s/\(<price.*>\)[^<>]*\(<\/price.*\)/\1$PRICE\2/" $XML_FILE_TO_MODIFY

This will replace the price with 123.

That sed command seems daunting, so let me break it down:

\(<price.*>\)[^<>]*\(<\/price.*\) is the pattern to match. \( ... \) are parenthesis for grouping. <price.*> matches the opening price tag. [^<>]* matches anything except angle brackets, and in this case will match the contents of the price tag. <\/price.* matches the end of the price tag. Forward slash is a delimiter in sed, so I escape it with a back slash.

\1$PRICE\2 is the text to replace the matched text with. \1 refers to the first matched parenthesis group, which is the opening price tag. $PRICE is the variable with the desired price in it. \2 refers to the second parenthesis group, in this case the closing tag.

Solution 3

I did not have the luxury of having xmlstarlet. I found a solution though simply by doing an inline replacement;

template-parameter.xml

<ns:Parameter>
    <ns:Name required="true">##-ParamName-##</ns:Name>
    <ns:Value>
        <ns:Text>##-ParamValue-##</ns:Text>
    </ns:Value>
</ns:Parameter>

Snippet

tokenName="foo"
tokenValue="bar"    

#Replace placeholders in parameter template element
myParamElement=$(cat template-parameter.xml)
myParamElement=${myParamElement//##-ParamName-##/$tokenName}
myParamElement=${myParamElement//##-ParamValue-##/$tokenValue}  

Result

<ns:Parameter>
    <ns:Name required="true">foo</ns:Name>
    <ns:Value>
        <ns:Text>bar</ns:Text>
    </ns:Value>
</ns:Parameter>
Share:
22,003

Related videos on Youtube

Clucky
Author by

Clucky

Updated on June 05, 2020

Comments

  • Clucky
    Clucky almost 4 years

    Quick Summary: I need to create a Bash script to change the text within a node automatically every week. The script will match the node and replace the text inside them (if this is possible)? How would I do this?

    Long Summary: I host a Minecraft server which has shops, each of which have their own .xml file in the /ShowcaseStandalone/ffs-storage/ directory. Every Sunday my server restarts and executes several commands into the terminal to reset several things. One thing that I am trying to make change is one of the shops. I am wanting to change the text in the node <itemstack> and the text in the node <price>. I am simply wanting to take text from a .txt file in a different folder, and insert it into that node. The problem is, that the text in the node will change every week. Is there any way to replace a specific line or text within two nodes using bash?

    XML file:

    <?xml version="1.0" encoding="UTF-8"?>
    <scs-shop usid="cac8480951254352116d5255e795006252d404d9" version="2" type="storage">
        <enchantments type="string"/>
        <owner type="string">Chadward27</owner>
        <world type="string">Frisnuk</world>
        <itemStack type="string">329:0</itemStack>
        <activity type="string">BUY</activity>
        <price type="double">55.0</price>
        <locX type="double">487.5</locX>
        <locY type="double">179.0</locY>
        <locZ type="double">-1084.5</locZ>
        <amount type="integer">0</amount>
        <maxAmount type="integer">0</maxAmount>
        <isUnlimited type="boolean">true</isUnlimited>
        <nbt-storage usid="23dffac5fb2ea7cfdcf0740159e881026fde4fa4" version="2" type="storage"/>
    </scs-shop>
    

    Operating System: Linux Ubuntu 12.04

    • Gilles Quenot
      Gilles Quenot over 11 years
      So you just want to change usid from scs-shop tag ?
    • Brian Cain
      Brian Cain over 11 years
      XML manipulation using exclusively bash? Why limit yourself?
    • Clucky
      Clucky over 11 years
      @sputnick Sorry, I had to ad &lt and &gt tags because it killed my nodes, but no, I need to replace the text in the nodes "itemstack" and "price"
    • Clucky
      Clucky over 11 years
      @Brian-Cain Is there any other way to do this from terminal? Because these commands all execute upon the server shutting off
    • Brian Cain
      Brian Cain over 11 years
      I'd recommend python -- does your server have python installed, or can it? If not, java might be another option.
    • Clucky
      Clucky over 11 years
      @Brian-Cain I believe it does have python and it most certainly has Java. I heard you can do this using perl also; however, the only way I think perl would work for this specific script is if I also used RegEx (something I do not know). Also, am I tagging correctly? I'm brand new at this website, although I use it often for script references.
    • tripleee
      tripleee over 8 years
      The duplicate stackoverflow.com/questions/23560215/replace-xml-value-with-‌​sed has a few more answers if the ones here are unsuitable for some reason.
  • Clucky
    Clucky over 11 years
    I don't mean to make you do all the work for me, but I am a visual learner. How could I use this to replace "<price type="double">55.0<price>" with "<price type="double">25.0<price>"?
  • Gilles Quenot
    Gilles Quenot over 11 years
    I can give you the full syntax if you give me a valid XML
  • Gilles Quenot
    Gilles Quenot over 11 years
    You can test it like this : xmlstarlet val file.xml
  • Clucky
    Clucky over 11 years
    There, I fixed it. Sorry I'm brand new to stackoverflow so I don't know how to format correctly. But it should be a valid xml file now.
  • Gilles Quenot
    Gilles Quenot over 11 years
    Post edited with an example for price tag, it's quite very simple to adapt it for other tags
  • Clucky
    Clucky over 11 years
    Worked like a charm. Thank you so much!
  • Gilles Quenot
    Gilles Quenot over 11 years
    This is the wrong way. You can't realistically parse tag-based markup languages like HTML or XML using Bash, grep, sed, cut, etc. See codinghorror.com/blog/archives/001311.html (you know the blog website from SO creator ?) This is about HTML, but this is the same for XML...
  • Dan Bliss
    Dan Bliss over 11 years
    If linux came with a good xml tool like xmlstarlet then I would agree that this is the wrong way. Until it does, sed is my go-to tool. Realistically, you can use sed in 99% of cases, including this one.
  • Gilles Quenot
    Gilles Quenot over 11 years
    I don't said "that don't work" but : "that's not reliable". In a simple case like this this seems "fine" for some people, but what happens when the XML will becomes more complicated with nested tags ? I'm talking about reliable way to makes things clean. sudo apt-get install xmlstarlet is not a big deal there.
  • Dan Bliss
    Dan Bliss over 11 years
    "but I think that's just as wrongheaded as demanding every trivial HTML processing task be handled by a full-blown parsing engine" is a quote from the blog post you cite. Regardless, for the record: I prefer the xmlstarlet solution. But if xmlstarlet is not available, sed will get er done.
  • BeniBela
    BeniBela over 11 years
    @sputnick: apt-get install is not so easy, e.g. you need a internet connection and free space on the HD (if you use something like Backtrack you have a 4GB ramdisk and if it is full, it crashes). And recently my apt system broke, and I could not use apt-get install for anything for 3 months!
  • Clucky
    Clucky over 11 years
    It is definately good to have both of these resources at my hands in case one fails. Thank you btw for the use of regular expression, that was where i was initially getting at with this post. I do enjoy the more simplistic design of xmlstarlet
  • tripleee
    tripleee over 7 years
    The blanket wildcard .* here is definitely dangerous. Try to limit matching with [^<>]* to avoid spanning across tags. There are still many corner cases where this could fail; for example, many XML schemata will allow a line break before a close tag, which will completely break one of the fundamental assumptions of this solution. Altogether, these points illustrate exactly why this approach is problematic.
  • Dan Bliss
    Dan Bliss over 7 years
    Agree that excluding the angle brackets is better.
  • Sirojiddin Komolov
    Sirojiddin Komolov about 5 years
    Then how to change the result file again?