Search code examples
xmlunixshxmlstarlet

Xmlstarlet / Sh - Copy content of a node into a new node / concat existing


I have the following xml (as example)

<?xml version="1.0"?>
<gameList>
    <game>
        <path>./sunsetbl.zip</path>
        <name>sunsetbl</name>
        <playcount>8</playcount>
        <lastplayed>20180924T214132</lastplayed>
    </game>
    <game>
        <path>./ssriders.zip</path>
        <name>Sunset Riders (4 Players ver EAC)</name>
        <playcount>4</playcount>
        <lastplayed>20181030T013801</lastplayed>
    </game>
    <game>
        <path>./kof97.zip</path>
        <name>The King of Fighters '97 (NGM-2320)</name>
        <playcount>2</playcount>
        <lastplayed>20181030T035949</lastplayed>
    </game>
    <game>
        <path>./dino.zip</path>
        <name>Cadillacs and Dinosaurs (World 930201)</name>
        <favorite>true</favorite>
        <playcount>26</playcount>
        <lastplayed>20181030T043441</lastplayed>
    </game>
    <game>
        <path>./kof98n.zip</path>
        <name>kof98n</name>
        <playcount>1</playcount>
        <lastplayed>20181031T001024</lastplayed>
    </game>
</gameList>

Well, I'm trying to do a sh script and use xmlstarlet on it. For each game node I want to copy the path node and insert it into a new node called description, and if description already exists, concatenate it to the existing text there.

I'm really noob on this and this is what I was able to do until now.

#!/bin/bash

set -e
shopt -s nullglob

for file in *.xml
do
  FILENAME=`xmlstarlet sel -t -m "/gameList/game" -v path $file`;
  echo "Appending $FILENAME into description on $file";
  xmlstarlet ed -L -s "/gameList/game" -t elem -n description -v "$FILENAME" $file;
done

Of course, the results sucks, all the path values are together in one line and copied to each game node, this is what it shows

<?xml version="1.0"?>
<gameList>
  <game>
    <path>./sunsetbl.zip</path>
    <name>sunsetbl</name>
    <playcount>8</playcount>
    <lastplayed>20180924T214132</lastplayed>
    <description>./sunsetbl.zip./ssriders.zip./kof97.zip./dino.zip./kof98n.zip</description>
  </game>
  <game>
    <path>./ssriders.zip</path>
    <name>Sunset Riders (4 Players ver EAC)</name>
    <playcount>4</playcount>
    <lastplayed>20181030T013801</lastplayed>
    <description>./sunsetbl.zip./ssriders.zip./kof97.zip./dino.zip./kof98n.zip</description>
  </game>
  <game>
    <path>./kof97.zip</path>
    <name>The King of Fighters '97 (NGM-2320)</name>
    <playcount>2</playcount>
    <lastplayed>20181030T035949</lastplayed>
    <description>./sunsetbl.zip./ssriders.zip./kof97.zip./dino.zip./kof98n.zip</description>
  </game>
  <game>
    <path>./dino.zip</path>
    <name>Cadillacs and Dinosaurs (World 930201)</name>
    <favorite>true</favorite>
    <playcount>26</playcount>
    <lastplayed>20181030T043441</lastplayed>
    <description>./sunsetbl.zip./ssriders.zip./kof97.zip./dino.zip./kof98n.zip</description>
  </game>
  <game>
    <path>./kof98n.zip</path>
    <name>kof98n</name>
    <playcount>1</playcount>
    <lastplayed>20181031T001024</lastplayed>
    <description>./sunsetbl.zip./ssriders.zip./kof97.zip./dino.zip./kof98n.zip</description>
  </game>
</gameList>

I would gladly appreciate any help on this.


Solution

  • If you can use a separate XSLT file, the solution would be as follows.
    Use this XSLT file (here named trans.xslt):

    <?xml version="1.0"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" />
    
        <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*" />
            </xsl:copy>
        </xsl:template>
    
        <!-- Add 'path' value to existing 'description' element -->
        <xsl:template match="description">    
          <xsl:copy>
            <xsl:value-of select="concat(.,../path)" />
          </xsl:copy>
        </xsl:template>
    
        <!-- Create new 'description' element -->
        <xsl:template match="game[not(description)]">  
          <xsl:copy>
            <xsl:copy-of select="node()|@*" />
            <description>
                <xsl:value-of select="path" />
            </description>
          </xsl:copy>
        </xsl:template>
    
    </xsl:stylesheet>
    

    And call it with this script:

    #!/bin/bash
    set -e
    shopt -s nullglob
    
    for file in *.xml
    do
      xmlstarlet tr trans.xslt "$file" > "$file.new.xml"
    done
    

    This create new files with the extension filename.new.xml.