【问题标题】:Parse xml using shell tools like grep, awk or sed [duplicate]使用 grep、awk 或 sed 等 shell 工具解析 xml [重复]
【发布时间】:2017-09-08 12:34:32
【问题描述】:

我有以下 xml 来根据标签的值解析和提取标签的值。仅当 type == 'hosted' 时才提取。我想使用 grep、sed 和 awk 等 bash 工具进行提取。没有条件提取单个标签值是我以前做过的事情,而不是条件。我可以使用 python 或我知道的任何其他编程语言轻松完成它。但如果在 shell 脚本中完成,这将是理想的。

...
    <repositories-item>
      <name>hosted-npm</name>
      <type>hosted</type>
    </repositories-item>
    <repositories-item>
      <name>proxied-npm</name>
      <type>proxied</type>
    </repositories-item>
...

【问题讨论】:

  • 我强烈建议您使用 XML 工具来处理 XML(在 Python 等中也是如此)。特别是,您可能会考虑编写适当的 XSLT 样式表,并使用脚本中的命令(例如 xsltproc)将样式表应用于 XML 输入。
  • 查看xmlstarlet

标签: bash shell awk sed


【解决方案1】:

缺少 xml 特定工具

awk 使用封闭标签来定义记录分隔符

$ awk -v RS='</?repositories-item>' '/<type>hosted<\/type>/' file

  <name>hosted-npm</name>
  <type>hosted</type>

请注意,这需要 GNU awk 支持的多字符 RS。

你可以对匹配和输出有更多的控制

$ awk -v RS='</?repositories-item>' -F'[<>]' '
    {delete a; 
     for(i=2;i<=NF;i+=4) a[$i]=$(i+1); 
     if(a["type"]=="hosted") print a["name"] }' file


hosted-npm

【讨论】:

    【解决方案2】:

    xmlstarlet 是一个命令行 XML 工具包,可以将复杂的 XSLT 模板表示为命令行开关的短序列。

    假设我们得到了一个格式良好的 XML 文档repos.xml

    <repositories>
      <repositories-item>
          <name>hosted-npm</name>
          <type>hosted</type>
        </repositories-item>
        <repositories-item>
          <name>proxied-npm</name>
          <type>proxied</type>
        </repositories-item>
    </repositories>
    

    如果您通过带有以下开关的 XMLStarlet 过滤器运行它

    $ cat repos.xml | xmlstarlet sel -t -m '//repositories-item' \
                     -i 'type="hosted"' -v 'name' -n 
    

    你会得到一行输出

    hosted-npm
    

    让我们看看 XMLStarlet 命令行。

    1. 我们在使用sel 开关指定的选择模式下运行命令
    2. 我们使用-t 开关指定选择模板
    3. 我们使用//repositories-item 模板将解析器限制为&lt;repositories-item&gt; 元素,该模板由-m 开关指定
    4. 我们只选择“托管”这些元素作为type 元素的值,该元素由-i 开关指定
    5. 我们打印出name 元素的值,由-v 开关指定。
    6. 在每行输出之后,我们打印一个用-n 开关指定的换行符。

    这是由 XMLStarlet 生成的等效 XSLT

    <?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="//repositories-item">
          <xsl:choose>
            <xsl:when test="type=&quot;hosted&quot;">
              <xsl:call-template name="value-of-template">
                <xsl:with-param name="select" select="name"/>
              </xsl:call-template>
              <xsl:value-of select="'&#10;'"/>
            </xsl:when>
          </xsl:choose>
        </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()&gt;1]">
          <xsl:value-of select="'&#10;'"/>
          <xsl:value-of select="."/>
        </xsl:for-each>
      </xsl:template>
    </xsl:stylesheet>
    

    根据 Charles Duffy 的建议,值得注意的是,可以使用 XMLStarlet 使用 -C 选项生成此 XSLT 规范:

    xmlstarlet sel -C -t -m '//repositories-item' \
           -i 'type="hosted"' -v 'name' -n > hosted-repos.xslt
    

    这个生成的XSLT规范可以直接和xsltproc一起使用

    cat repos.xml | xsltproc hosted-repos.xslt - 
    

    【讨论】:

    • 可能值得向 OP 展示如何使用 xsltproc 来应用 XSLT,以确保他们了解他们可以在未安装 XMLStarlet 的服务器上使用此解决方案。
    猜你喜欢
    • 2022-11-30
    • 1970-01-01
    • 2011-07-19
    • 2016-03-05
    • 2011-12-27
    • 2018-08-23
    • 2012-07-11
    • 2017-09-19
    • 1970-01-01
    相关资源
    最近更新 更多