我使用xmlstarlet el -v
来显示xml文件的结构,包括所有属性和值。我想将其输出转换为某种键值对,即每个属性的值在一个单独的行上(包括XPath);每一行必须是唯一的。
目前的结果:
topRoot/topSystem/commSvcEp/commSyslog[@descr='Syslog Service' and @name='syslog' and @policyOwner='local' and @severity='critical']
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[@adminState='disabled' and @forwardingFacility='local7' and @hostname='none' and @name='secondary' and @severity='critical']
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[@adminState='disabled' and @forwardingFacility='local7' and @hostname='none' and @name='tertiary' and @severity='critical']
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[@adminState='disabled' and @forwardingFacility='local7' and @hostname='none' and @name='primary' and @severity='critical']
期望的结果(可能是类似的;指数只是一个想法):
topRoot/topSystem/commSvcEp/commSyslog@descr='Syslog Service'
topRoot/topSystem/commSvcEp/commSyslog@name='syslog'
topRoot/topSystem/commSvcEp/commSyslog@policyOwner='local'
topRoot/topSystem/commSvcEp/commSyslog@severity='critical'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[0]@adminState='disabled'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[0]@forwardingFacility='local7'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[0]@hostname='none'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[0]@name='secondary'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[0]@severity='critical'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[1]@adminState='disabled'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[1]@forwardingFacility='local7'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[1]@hostname='none'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[1]@name='tertiary'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[1]@severity='critical'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[2]@adminState='disabled'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[2]@forwardingFacility='local7'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[2]@hostname='none'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[2]@name='primary'
topRoot/topSystem/commSvcEp/commSyslog/commSyslogClient[2]@severity='critical'
我正在努力实现的目标是能够在两个此类文件上运行diff
或使用grep
来过滤匹配模式。我确信有一种方法可以创建此类输出,而不使用sed
,awk
或除xmlstarlet
之外的任何其他内容。
我几乎是关于xmlstarlet和整个xml世界的新手(不仅仅因为它因为它的复杂性和解析开销等而不喜欢xml),所以我非常感谢你的帮助。谢谢!
答案 0 :(得分:5)
由于您已经在使用xmlstarlet
,因此您也可以使用XSLT。
XSLT 1.0 (可以使用tr
命令与xmlstarlet一起运行)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:param name="sep" select="'.'"/>
<xsl:template match="text()"/>
<xsl:template match="*">
<xsl:variable name="path">
<xsl:for-each select="ancestor-or-self::*">
<xsl:variable name="predicate">
<xsl:call-template name="genPredicate"/>
</xsl:variable>
<xsl:if test="ancestor::*">
<xsl:value-of select="$sep"/>
</xsl:if>
<xsl:value-of select="concat(local-name(),$predicate)"/>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="@*">
<xsl:value-of select="concat($path,$sep,name(),'=',.)"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:if test="not(@*)">
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template name="genPredicate">
<xsl:if test="preceding-sibling::*[local-name()=local-name(current())] or following-sibling::*[local-name()=local-name(current())]">
<xsl:value-of select="concat('[',count(preceding-sibling::*[local-name()=local-name(current())])+1,']')"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
注意:有一个名为sep
的参数,因此您可以从命令行更改分隔符(使用-p sep="/"
或您想要的任何分隔符)。它目前设置为您首选的“。”。
<强>输出强>
commSyslog.descr=Syslog Service
commSyslog.name=syslog
commSyslog.policyOwner=local
commSyslog.severity=critical
commSyslog.commSyslogClient[1].adminState=disabled
commSyslog.commSyslogClient[1].forwardingFacility=local7
commSyslog.commSyslogClient[1].hostname=none
commSyslog.commSyslogClient[1].name=secondary
commSyslog.commSyslogClient[1].severity=critical
commSyslog.commSyslogClient[2].adminState=disabled
commSyslog.commSyslogClient[2].forwardingFacility=local7
commSyslog.commSyslogClient[2].hostname=none
commSyslog.commSyslogClient[2].name=tertiary
commSyslog.commSyslogClient[2].severity=critical
commSyslog.commSyslogClient[3].adminState=disabled
commSyslog.commSyslogClient[3].forwardingFacility=local7
commSyslog.commSyslogClient[3].hostname=none
commSyslog.commSyslogClient[3].name=primary
commSyslog.commSyslogClient[3].severity=critical
commSyslog.commSyslogMonitor.adminState=disabled
commSyslog.commSyslogMonitor.descr=
commSyslog.commSyslogMonitor.name=
commSyslog.commSyslogMonitor.severity=critical
commSyslog.commSyslogConsole.adminState=disabled
commSyslog.commSyslogConsole.descr=
commSyslog.commSyslogConsole.name=
commSyslog.commSyslogConsole.severity=critical
commSyslog.commSyslogSource.audits=disabled
commSyslog.commSyslogSource.descr=
commSyslog.commSyslogSource.events=disabled
commSyslog.commSyslogSource.faults=enabled
commSyslog.commSyslogSource.name=
commSyslog.commSyslogFile.adminState=enabled
commSyslog.commSyslogFile.descr=
commSyslog.commSyslogFile.name=messages
commSyslog.commSyslogFile.severity=critical
commSyslog.commSyslogFile.size=4194304
答案 1 :(得分:1)
我决定使用libxml2编写一个小程序,它解析XML并以所需格式递归输出。
可以使用以下命令编译代码,或类似的东西(我决定将其称为xmlkv, kv 代表键值):
cc -o xmlkv xmlkv.c -s -Os -Wall -Wextra `xml2-config --cflags --libs`
代码:
#include <stdlib.h>
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
static void print_elements(xmlNode *);
static void
print_elements(xmlNode *node)
{
xmlNode *n;
xmlAttrPtr a;
xmlChar *v, *p, *q;
for (n = node; n; n = n->next) {
if (n->type == XML_ELEMENT_NODE) {
if ((p = xmlGetNodePath(n)) == NULL)
return;
for (q = p; *q; q++)
if (*q == '/')
*q = '.';
for (a = n->properties; a; a = a->next) {
if ((v = xmlGetProp(n, a->name)) == NULL)
return;
printf("%s.%s=%s\n", p + 1, a->name, v);
xmlFree(v);
}
xmlFree(p);
}
print_elements(n->children);
}
}
int
main(int argc, char **argv)
{
xmlDoc *doc = NULL;
xmlNode *root = NULL;
LIBXML_TEST_VERSION
if (argc != 2) {
fprintf(stderr, "usage: %s <file>\n", *argv);
return EXIT_FAILURE;
}
if ((doc = xmlReadFile(argv[1], NULL, 0)) == NULL)
return EXIT_FAILURE;
if ((root = xmlDocGetRootElement(doc)) == NULL)
return EXIT_FAILURE;
print_elements(root);
xmlFreeDoc(doc);
xmlCleanupParser();
return EXIT_SUCCESS;
}
测试:
$ cat test.xml
<commSyslog descr="Syslog Service" name="syslog" policyOwner="local" severity="critical">
<commSyslogClient adminState="disabled" forwardingFacility="local7" hostname="none" name="secondary" severity="critical"/>
<commSyslogClient adminState="disabled" forwardingFacility="local7" hostname="none" name="tertiary" severity="critical"/>
<commSyslogClient adminState="disabled" forwardingFacility="local7" hostname="none" name="primary" severity="critical"/>
<commSyslogMonitor adminState="disabled" descr="" name="" severity="critical"/>
<commSyslogConsole adminState="disabled" descr="" name="" severity="critical"/>
<commSyslogSource audits="disabled" descr="" events="disabled" faults="enabled" name=""/>
<commSyslogFile adminState="enabled" descr="" name="messages" severity="critical" size="4194304"/>
</commSyslog>
$ ./xmlkv test.xml
commSyslog.descr=Syslog Service
commSyslog.name=syslog
commSyslog.policyOwner=local
commSyslog.severity=critical
commSyslog.commSyslogClient[1].adminState=disabled
commSyslog.commSyslogClient[1].forwardingFacility=local7
commSyslog.commSyslogClient[1].hostname=none
commSyslog.commSyslogClient[1].name=secondary
commSyslog.commSyslogClient[1].severity=critical
commSyslog.commSyslogClient[2].adminState=disabled
commSyslog.commSyslogClient[2].forwardingFacility=local7
commSyslog.commSyslogClient[2].hostname=none
commSyslog.commSyslogClient[2].name=tertiary
commSyslog.commSyslogClient[2].severity=critical
commSyslog.commSyslogClient[3].adminState=disabled
commSyslog.commSyslogClient[3].forwardingFacility=local7
commSyslog.commSyslogClient[3].hostname=none
commSyslog.commSyslogClient[3].name=primary
commSyslog.commSyslogClient[3].severity=critical
commSyslog.commSyslogMonitor.adminState=disabled
commSyslog.commSyslogMonitor.descr=
commSyslog.commSyslogMonitor.name=
commSyslog.commSyslogMonitor.severity=critical
commSyslog.commSyslogConsole.adminState=disabled
commSyslog.commSyslogConsole.descr=
commSyslog.commSyslogConsole.name=
commSyslog.commSyslogConsole.severity=critical
commSyslog.commSyslogSource.audits=disabled
commSyslog.commSyslogSource.descr=
commSyslog.commSyslogSource.events=disabled
commSyslog.commSyslogSource.faults=enabled
commSyslog.commSyslogSource.name=
commSyslog.commSyslogFile.adminState=enabled
commSyslog.commSyslogFile.descr=
commSyslog.commSyslogFile.name=messages
commSyslog.commSyslogFile.severity=critical
commSyslog.commSyslogFile.size=4194304
看起来不错,正是我想要的。当然,用/
替换.
几乎没用,但我更喜欢这种符号,因为它在眼睛上更好。