使用unix script /命令将xml文件中的值提取为字段分隔符格式

时间:2012-07-26 07:57:07

标签: xml shell unix xml-parsing

这是示例文件,我们需要将值转换为分隔符格式的文件: -

的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的情况下执行此操作?

3 个答案:

答案 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手动解析它,但这样的解决方案不是理想的 ,尤其是输入不一致。例如,以下(假设您有gawksed)接受您的输入并应吐出预期的输出:

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>&#xA;</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解析/转换的其他答案那样灵活/可靠。

虽然可以在某种程度上加强......