Reputation: 3727
I can see how I can retrieve all attribute values:
xml sel -t -v "//element/@*"
but I want to get all attribute names.
I can get the n'th name by, for example xml sel -t -v "name(//x:mem/@*[3])"
which returns the 3rd attribute name.
but xml sel -t -v "name(//x:mem/@*)"
does not work (returns the 1st attribute name only)...
Upvotes: 3
Views: 7480
Reputation: 22617
Use -t
and -m
to define a template match and then apply another XPath expression with -v
.
$ xml sel -T -t -m "//mem/@*" -v "name()" -n input.xml
when applied to this input XML:
<root>
<mem yes1="1" yes2="2"/>
<other no="1" no2="2"/>
</root>
will print:
yes1
yes2
That's a "short line on the shell", but it's completely unintelligible. Therefore, I would still prefer kjhughes' XSLT solution. Do not sacrifice understandable code in favour of brevity.
You could write a stylesheet that takes an input parameter from the command line, so that you do not have to change the XSLT code if you'd like to retrieve the attribute names of a different element.
As suggested by @npostavs, internally, xmlstarlet makes use of XSLT anyway. You can inspect the XSLT that is generated by replacing -T
with -C
:
$ xml sel -C -t -m "//mem/@*" -v "name()" -n app.xml
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<xsl:for-each select="//mem/@*">
<xsl:call-template name="value-of-template">
<xsl:with-param name="select" select="name()"/>
</xsl:call-template>
<xsl:value-of select="' '"/>
</xsl:for-each>
</xsl:template>
<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="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
There are many more options to explore, see the xmlstarlet documentation.
Upvotes: 4
Reputation: 111511
This xmlstarlet command:
xml tr attnames.xsl in.xml
Using this XSLT transform, named attnames.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="@*">
<xsl:value-of select="name(.)"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="*">
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="*"/>
</xsl:template>
</xsl:stylesheet>
And this XML file, named in.xml:
<root att1="one">
<a att2="two"/>
<b att3="three">
<c att4="four"/>
</b>
</root>
Will produce a list of all attributes found in in.xml:
att1
att2
att3
att4
To select all attributes from the b
element only, modify the XSLT like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="@*">
<xsl:value-of select="name(.)"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="b">
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Upvotes: 3