我有以下输入xml文件:
<rootnode>
<section id="1" status="fail">
<outer status="fail">
<inner status="fail"/>
<inner status="pass"/>
</outer>
<outer status="pass">
<inner status="pass"/>
</outer>
<outer status="pass"/>
<outer status="fail"/>
</section>
<section id="2" status="fail">
<outer status="fail">
<inner status="pass"/>
<inner status="fail"/>
<inner status="inc"/>
</outer>
</section>
</rootnode>
我想过滤掉所有非失败状态节点,以便结果如下所示:
<rootnode>
<section id="1" status="fail">
<outer status="fail">
<inner status="fail"/>
</outer>
<outer status="fail"/>
</section>
<section id="2" status="fail">
<outer status="fail">
<inner status="fail"/>
</outer>
</section>
</rootnode>
<rootnode>
不一定必须包含在结果中。我试图将xmllint
与xpath表达式一起使用。我可以用
xmllint --xpath "//inner" input.xml
xmllint --xpath "//@status" input.xml
但它们只返回节点而不考虑status
的值,或者只返回没有周围节点的属性。
有没有办法用xpath表达式做到这一点?如果没有,那么包含其他bash工具的简单解决方案也很好。
答案 0 :(得分:2)
就像@svasa在评论中所说,你应该使用XSLT。您可以使用xsltproc,xmlstarlet(使用tr
命令),Saxon(command line上的java)等轻松处理bash中的XSLT。
以下是使用xsltproc的示例:
$ xsltproc so.xsl so.xml
<?xml version="1.0"?>
<rootnode>
<section id="1" status="fail">
<outer status="fail">
<inner status="fail"/>
</outer>
<outer status="fail"/>
</section>
<section id="2" status="fail">
<outer status="fail">
<inner status="fail"/>
</outer>
</section>
</rootnode>
XML输入(so.xml)
<rootnode>
<section id="1" status="fail">
<outer status="fail">
<inner status="fail"/>
<inner status="pass"/>
</outer>
<outer status="pass">
<inner status="pass"/>
</outer>
<outer status="pass"/>
<outer status="fail"/>
</section>
<section id="2" status="fail">
<outer status="fail">
<inner status="pass"/>
<inner status="fail"/>
<inner status="inc"/>
</outer>
</section>
</rootnode>
XSLT 1.0 (so.xsl)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@status[not(normalize-space()='fail')]]"/>
</xsl:stylesheet>
如果你不介意,我会有一个小的后续问题。当。。。的时候 input.xml文件不包含任何status = fail节点,然后是输出 只有两行:
<?xml version="1.0"?>
和<rootnode/>
。是吗 在这种情况下,可能两个完全抑制输出?它不是 真的有问题,我知道如何在bash中解决它。我只是 感兴趣,如果通过xslt有一个干净的解决方案。
您可以做的是省略XML声明(omit-xml-declaration="yes"
中的xsl:output
)并检查是否存在status="fail"
的任何元素。我会使用密钥(xsl:key
)...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes">
<!--If you need to output the declaration when there
are elements with status="fail", it might be best to post process files that
only contain the xml declaration.-->
</xsl:output>
<xsl:strip-space elements="*"/>
<!--Key of all elements with status="fail".-->
<xsl:key name="fails" match="*[@status='fail']" use="@status"/>
<xsl:template match="/*[not(key('fails','fail'))]">
<!--If there aren't any elements with status="fail", don't process
anything else.-->
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@status[not(normalize-space()='fail')]]"/>
</xsl:stylesheet>