xmlstarlet

How to print the location of an XPath match with xmlstarlet?


I would like to find references to SNAPSHOT versions in a pom.xml file. Let's use the POM file located here for an example. I came up with the following command to find elements containing the string SNAPSHOT:

$ xmlstarlet sel -t -m "//*[contains(text(), 'SNAPSHOT')]" -v . -n pom.xml
0.2-SNAPSHOT
4.12-SNAPSHOT
1.9.13-SNAPSHOT
20.0-SNAPSHOT

This, however, as can be seen above, only gives me the text of the matches themself. What I would like to see is some more context regarding the location of the matches, for example the path leading to the matching elements, like this:

$ xmlstarlet magical arguments
/project/version: 0.2-SNAPSHOT
/project/dependencies/dependency: 4.12-SNAPSHOT
/project/properties/jackson.version: 1.9.13-SNAPSHOT
/project/properties/guava.version: 20.0-SNAPSHOT

Alternatively, having a stripped-down version of the XML as the output would also work for me, e.g.:

$ xmlstarlet magical arguments
<project>
  <version>0.2-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <version>4.12-SNAPSHOT</version>
    </dependency>
  </dependencies>
  <properties>
    <jackson.version>1.9.13-SNAPSHOT</jackson.version>
    <guava.version>20.0-SNAPSHOT</guava.version>
  </properties>
</project>

Is it possible to print either of these or some other kind of indication of where the matches occured?


Solution

  • xmlstarlet can produce the requested output using the -b option which breaks the nesting:

    xmlstarlet sel -t \
        -m "//*[contains(text(),'SNAPSHOT')]" \
        -m 'ancestor::*' -v 'name()' -o '/' \
        -b -v "concat(name(),': ',.)" -n pom.xml
    

    Output:

    project/version: 0.2-SNAPSHOT
    project/dependencies/dependency/version: 4.12-SNAPSHOT
    project/properties/jackson.version: 1.9.13-SNAPSHOT
    project/properties/guava.version: 20.0-SNAPSHOT