20

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

7
  • So you just want to change usid from scs-shop tag ? Commented Nov 13, 2012 at 22:27
  • 2
    XML manipulation using exclusively bash? Why limit yourself? Commented Nov 13, 2012 at 22:28
  • @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" Commented Nov 13, 2012 at 22:32
  • @Brian-Cain Is there any other way to do this from terminal? Because these commands all execute upon the server shutting off Commented Nov 13, 2012 at 22:33
  • I'd recommend python -- does your server have python installed, or can it? If not, java might be another option. Commented Nov 13, 2012 at 22:36

3 Answers 3

19

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
Sign up to request clarification or add additional context in comments.

5 Comments

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>"?
I can give you the full syntax if you give me a valid XML
You can test it like this : xmlstarlet val file.xml
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.
Post edited with an example for price tag, it's quite very simple to adapt it for other tags
16

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.

8 Comments

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...
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.
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.
"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.
@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!
|
2

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>

1 Comment

Then how to change the result file again?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.