Here is an xmlstarlet command line to get a node attribute and the child node contents for nodes that contain a certain child node.

Imagine you have an xml document like:

        <testcase classname="testid-1">
        <testcase classname="testid-2">
          <failure>Memory Error 22</failure>
        <testcase classname="testid-3">
          <failure>Invalid json</failure>
        <testcase classname="testid-4">

(Why yes, that _is_ an xunit xml data file.)

And you want to get the names of the tests that failed, along with the failure message. The xmlstarlet command line could be:

    xmlstarlet sel -B -t -m '//testcase[failure]' -v "concat(@classname, ' ', failure, '
')" foo.xml

(Note the newline as the last option to concat — it is an actual newline on the command line.)

The xslt generated from that line is (add the -C option to get this):

$ xmlstarlet sel -C -B -t -m '//testcase[failure]' -v "concat(@classname, ' ', failure, '
')" foo.xml
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="" xmlns:exslt="" version="1.0" extension-element-prefixes="exslt">
  <xsl:output omit-xml-declaration="yes" indent="no"/>
  <xsl:template match="/">
    <xsl:for-each select="//testcase[failure]">
      <xsl:call-template name="value-of-template">
        <xsl:with-param name="select" select="concat(@classname, ' ', failure, '
  <xsl:template name="value-of-template">
    <xsl:param name="select"/>
    <xsl:value-of select="$select"/>
    <xsl:for-each select="exslt:node-set($select)[position()>1]">
      <xsl:value-of select="'
      <xsl:value-of select="."/>

Hmm. I don’t know why there are two for-each loops there. I fear I will never fully grok xml and xmlstarlet.

Anyway, the output of that xmlstarlet command is:

$ xmlstarlet sel -B -t -m '//testcase[failure]' -v "concat(@classname, ' ', failure, '
')" foo.xml
testid-2 Memory Error 22
testid-3 Invalid json

The xmlstarlet documentation.