这是示例文件,我们需要将值转换为分隔符格式的文件: -
的test.xml
<?xml version="1.0" encoding="UTF-8" ?>
<testjar>
<testable>
<trigger>Trigger1</trigger>
<message>2012-06-14T00:03.54</message>
<sales-info>
<san-a>no</san-a>
<san-b>no</san-b>
<san-c>no</san-c>
</sales-info>
</testable>
<testable>
<trigger>Trigger2</trigger>
<message>2012-06-15T00:03.54</message>
<sales-info>
<san-a>yes</san-a>
<san-b>yes</san-b>
<san-c>no</san-c>
</sales-info>
</testable>
</testjar>
每条记录都应从新行开始。示例结果集应该是这样的 sample.txt的
Trigger1|2012-06-14T00:03.54|no|no|no
Trigger2|2012-06-15T00:03.54|yes|yes|no
注意: - 我的服务器上没有安装xmlstarlet,是否可以在没有xmlstarlet的情况下执行此操作?
答案 0 :(得分:1)
如果您安装了xmlstarlet,可以尝试:
me@home$ xmlstarlet sel -t -m "//testable" -v trigger -o "|" -v message -o "|" -m sales-info -v san-a -o "|" -v san-b -o "|" -v san-c -n test.xml
Trigger1|2012-06-14T00:03.54|no|no|no
Trigger2|2012-06-15T00:03.54|yes|yes|no
命令细分:
xmlstarlet sel -t
-m "//testable" # match <testable>
-v trigger -o "|" # print out value of <trigger> followed by |
-v message -o "|" # print out value of <message> followed by |
-m sales-info # match <sales-info>
-v san-a -o "|" # print out value of <san-a> followed by |
-v san-b -o "|" # print out value of <san-b> followed by |
-v san-c # print out value of <san-c>
-n # print new line
test.xml # INPUT XML FILE
要定位<testable>
内变化的标记,您可以尝试以下方法返回所有叶节点的文本:
ma@home$ xmlstarlet sel -t -m "//testable" -m "descendant::*[not(*)]" -v 'text()' -i 'not(position()=last())' -o '|' -b -b -n test.xml
Trigger1|2012-06-14T00:03.54|no|no|no
Trigger2|2012-06-15T00:03.54|yes|yes|no
命令的结束:
xmlstarlet sel -t
-m "//testable" # match <testable>
-m "descendant::*[not(*)]" # match all leaf nodes
-v 'text()' # print text
-i 'not(position()=last())' -o '|' # print | if not last item
-b -b # break out of nested matches
-n # print new line
test.xml # INPUT XML FILE
如果您无法访问xmlstarlet
,请查看您拥有的其他工具。其他选项包括xsltproc(请参阅mzjn's answer)和xpath。
如果这些工具不可用,我建议使用更高级别的语言(Python,Perl),这样您就可以访问正确的XML库。
虽然可以使用regex
手动解析它,但这样的解决方案不是理想的 † ,尤其是输入不一致。例如,以下(假设您有gawk
和sed
)接受您的输入并应吐出预期的输出:
me@home$ gawk 'match($0, />(.*)</, a){printf("%s|",a[1])} /<\/testable>/{print ""}' test.xml | sed 's/.$//'
Trigger1|2012-06-14T00:03.54|no|no|no
Trigger2|2012-06-15T00:03.54|yes|yes|no
但是,如果输入格式发生变化,这将失败,因此不是我通常建议的解决方案。
答案 1 :(得分:1)
这是一个XSLT样式表,可以执行您想要的(保存在test.xsl中):
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="testable">
<xsl:value-of select='trigger'/><xsl:text>|</xsl:text>
<xsl:value-of select='message'/><xsl:text>|</xsl:text>
<xsl:value-of select='sales-info/san-a'/><xsl:text>|</xsl:text>
<xsl:value-of select='sales-info/san-b'/><xsl:text>|</xsl:text>
<xsl:value-of select='sales-info/san-c'/><xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
命令(这里我假设你安装了libxml2和libxslt; xsltproc是一个使用这些库的命令行工具):
xsltproc -o sample.txt test.xsl test.xml
sample.txt的内容:
Trigger1|2012-06-14T00:03.54|no|no|no
Trigger2|2012-06-15T00:03.54|yes|yes|no
答案 2 :(得分:1)
这是一个纯粹的bash解决方案:
egrep '<trigger>|<message>|<san-.>' test.xml | sed -e 's/<[^>]*>//g' | while read line; do [ $((++i % 5)) -ne 0 ] && echo -n "$line|" || echo $line ; done
但是,它只适用于样本中格式化的文件(单独行中的每个元素),它甚至不如涉及正确XML解析/转换的其他答案那样灵活/可靠。
虽然可以在某种程度上加强......