1

I'm really looking for some advice on the best approach to tackling this in bash.

I have a XML file with 1000's of entries that looks like this:

<?xml version="1.0"?>
<myList>
    <dataitem>
        <path>./5553 Subset 43d.zip</path>
        <name>5553 Subset 43d</name>
    </dataitem>
    <dataitem>
        <path>./Another file name here with spaces.zip</path>
        <name>Another file name here with spaces</name>
    </dataitem>
...

And I'd like to add an additional key to each <dataitem> using the <name> key's data with an mp4 extension, so it would look like this:

<?xml version="1.0"?>
<myList>
    <dataitem>
        <path>./5553 Subset 43d.zip</path>
        <name>5553 Subset 43d</name>
        <video>5553 Subset 43d.mp4</video>
    </dataitem>
    <dataitem>
        <path>./Another file name here with spaces.zip</path>
        <name>Another file name here with spaces</name>
        <video>Another file name here with spaces.mp4</video>
    </dataitem>
...

2 Answers 2

2

The right way with xmlstarlet tool:

xmlstarlet ed -s "//dataitem" -t elem -n video input.xml \
| xmlstarlet ed -u "//dataitem/video" -x "concat(./preceding-sibling::name/text(), '.mp4')"

The output should be as:

<?xml version="1.0"?>
<myList>
  <dataitem>
    <path>./5553 Subset 43d.zip</path>
    <name>5553 Subset 43d</name>
    <video>5553 Subset 43d.mp4</video>
  </dataitem>
  <dataitem>
    <path>./Another file name here with spaces.zip</path>
    <name>Another file name here with spaces</name>
    <video>Another file name here with spaces.mp4</video>
  </dataitem>
...
Sign up to request clarification or add additional context in comments.

1 Comment

This is such an elegant solution - much appreciated
1

Alternatively, consider XSLT, the special-purpose language designed to transform XML, with a bash call to xsltproc.

XSLT (save as .xsl file, a special .xml file)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
    </xsl:template>

    <xsl:template match="dataitem">
     <xsl:copy>
       <xsl:copy-of select="*"/>
       <video><xsl:value-of select="concat(name, '.mp4')"/></video>
     </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Bash (assuming local cd paths, using installed xsltproc)

xsltproc -o Output.xml XSLTScript.xsl Input.xml

1 Comment

A great solution but a little extravagant for my needs. It's good to see how I might approach this for larger projects though. Thank you

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.