XML & XPath
Process XML with xmllint and xmlstarlet — XPath queries, validation, and in-place editing.
xmllint — Query and Format
Pretty-print XML — reformat for readability
xmllint --format response.xml
XPath query — extract a specific element value
xmllint --xpath '//hostname/text()' inventory.xml
XPath with attribute selection — find elements by attribute
xmllint --xpath '//device[@role="core"]/hostname/text()' inventory.xml
XPath returning multiple values — each on its own line
xmllint --xpath '//device/hostname/text()' inventory.xml 2>/dev/null | tr -s '\n'
Validate XML against a DTD — check structural correctness
xmllint --valid --dtdvalid schema.dtd config.xml --noout
Validate well-formedness only — no schema required
xmllint --noout config.xml && echo "Well-formed" || echo "Malformed"
Extract XML from an API response — curl + xmllint pipeline
curl -s https://api.example.com/data.xml | xmllint --format --xpath '//result' -
xmlstarlet — Select, Edit, Validate
Select element text — equivalent to XPath text() extraction
xmlstarlet sel -t -v '//device/hostname' inventory.xml
Select with template — format output with custom separators
xmlstarlet sel -t -m '//device' -v 'hostname' -o ',' -v 'ip' -n inventory.xml
Select attributes — extract attribute values from elements
xmlstarlet sel -t -v '//device/@role' inventory.xml
Count matching elements — useful for validation scripts
xmlstarlet sel -t -v 'count(//device)' inventory.xml
Edit XML — update an element value in-place
xmlstarlet ed -u '//device[@name="sw-core"]/vlan' -v '20' inventory.xml
Edit XML — add a new child element
xmlstarlet ed -s '//device[@name="sw-core"]' -t elem -n "location" -v "rack-a1" inventory.xml
Edit XML — delete an element
xmlstarlet ed -d '//device[@name="decommissioned"]' inventory.xml
In-place editing — modify the file directly (like sed -i)
xmlstarlet ed -L -u '//config/timeout' -v '60' config.xml
Validate against XSD schema
xmlstarlet val -e -s schema.xsd config.xml
Common XPath Expressions
Root element children — /root/element
xmlstarlet sel -t -v '/inventory/device/hostname' inventory.xml
Any depth search — //element finds at any nesting level
xmlstarlet sel -t -v '//hostname' inventory.xml
Predicate filtering — [condition] narrows the selection
# By position
xmlstarlet sel -t -v '//device[1]/hostname' inventory.xml
# By child element value
xmlstarlet sel -t -v '//device[vlan=10]/hostname' inventory.xml
# By attribute
xmlstarlet sel -t -v '//device[@enabled="true"]/hostname' inventory.xml
Boolean operators in predicates — and, or, not()
xmlstarlet sel -t -v '//device[@role="access" and vlan > 10]/hostname' inventory.xml
XPath functions — string-length, contains, starts-with
# Elements containing a substring
xmlstarlet sel -t -v '//device[contains(hostname, "core")]/ip' inventory.xml
# Elements starting with a prefix
xmlstarlet sel -t -v '//device[starts-with(hostname, "sw-")]/hostname' inventory.xml
Namespace Handling
Query with default namespace — must register it first
xmlstarlet sel -N ns="http://example.com/schema" \
-t -v '//ns:device/ns:hostname' namespaced.xml
xmllint with namespace — use local-name() to ignore namespace prefix
xmllint --xpath '//*[local-name()="device"]/*[local-name()="hostname"]/text()' namespaced.xml
Strip namespaces entirely — simplify processing when namespace is noise
xmlstarlet ed -d '//@*[local-name()="schemaLocation"]' namespaced.xml \
| sed 's/ xmlns[^"]*"[^"]*"//g' \
| xmlstarlet sel -t -v '//device/hostname' -
XSLT Processing
Transform XML with an XSLT stylesheet — xsltproc
xsltproc transform.xsl input.xml > output.xml
Pass parameters to XSLT — inject values at transform time
xsltproc --stringparam environment "production" transform.xsl input.xml
Quick XSLT to extract CSV from XML — inline stylesheet
xsltproc - inventory.xml <<'XSLT'
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>hostname,ip,role </xsl:text>
<xsl:for-each select="//device">
<xsl:value-of select="hostname"/>,<xsl:value-of select="ip"/>,<xsl:value-of select="@role"/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XSLT
API Response Processing
Cisco ISE API — extract endpoint data from XML response
curl -sk -u admin:password https://ise-01:9060/ers/config/endpoint \
-H "Accept: application/xml" \
| xmllint --format --xpath '//resources/resource/name/text()' -
Parse SOAP response — extract the payload from the envelope
xmlstarlet sel -N soap="http://schemas.xmlsoap.org/soap/envelope/" \
-t -c '//soap:Body/*' response.xml | xmllint --format -
Extract error messages from XML API errors
xmlstarlet sel -t -v '//error/message' -n error_response.xml