【问题标题】:Get a tag value in multi line XML using shell script [duplicate]使用shell脚本获取多行XML中的标签值[重复]
【发布时间】:2019-05-30 06:52:41
【问题描述】:

我有一个xml文件如下

<Module dataPath="/abc/def/xyz" handler="DataRegistry" id="id1" path="test.so"/>
<Module id="id2" path="/my/file/path">
  <Config>
    <Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/xyz" />
    <Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/xyz" id="V2"/>
  </Config>
</Module>

我只想从每个moduleid 中提取dataPath 的值。

我用的,类似的命令

`grep 'id2' file | grep -ioPm1 "(?<=DataPath=)[^ ]+"`

这是从第一个模块 id 给我的,而不是第二个模块 id。因为第二个模块在多行中。

我如何使用 shell 脚本来做到这一点?

期望的输出是——如果我想得到 id1 模块的数据路径,那么应该得到 ​​p>

/my/file/path

对于第二个模块 id,比如 id2,我应该得到用逗号分隔的数据路径

/my/file/path, /my/file/path

或者我对数据路径进行 grep 的第二种方法是仅替换 &lt;Module&lt;/Module&gt; 之间的 newline character,然后我可以使用 grep。

【问题讨论】:

  • @jww,不,不是。我已经编辑了问题以更好地理解
  • 您想要的输出显示两行。第一行的值为 1 个dataPath 属性,第二行的值为 2 个dataPath 属性——每个属性用逗号分隔。大概该结构表明与您的源 xml 相关的模式?每个换行符是否等同于 &lt;Module&gt; 元素节点?即第一行表示有一个&lt;Module&gt; 与1 个关联的dataPath 属性,第二行表示有2 个dataPath 属性与另一个&lt;Module&gt; 关联。大概所有匹配值的列表(每行一个值)不是您想要的。请在 OP 中澄清。
  • @RobC,我有一个配置文件,其中包含每个 module 的配置(由 id 知道)。一些modules 的完整信息在一行中,而一些module 的信息在多行中。我只需要为每个module 提取一个标签datapath。这可能是一个 moduleid 可能有多个 datapath

标签: linux bash shell


【解决方案1】:

-m1 告诉 grep 在第一行匹配后退出,这就是它只打印一行输出的原因。
不过,我不会为此使用面向线的工具。解析 XML 有更方便的工具,例如 :

xml sel -t -m '//@dataPath' -v . -n file.xml

【讨论】:

  • xml sel -t -v '//@dataPath' -n file.xml
  • @Cyrus 如果没有具有dataPath 属性的元素,则会打印一个空行。
  • 在这种情况下,是的。如果不需要最后的换行符,也可以省略 -n
  • 这会将每个匹配的属性值打印在单独的行上。但是,我很确定 OP 希望对每行的值进行分组,即打印与 Module 元素节点关联的所有 dataPath 属性值(包括与后代节点关联的那些)并用逗号分隔它们。换行符(据我了解)应作为每个 Module 元素节点的分隔符。
【解决方案2】:

首先,我的回答假设您有实际格式良好的源 XML。您提供的示例代码没有根元素 - 但我假设有一个。

Bash 特性本身不太适合解析 XML。

这位著名的Bash FAQ 声明如下:

不要尝试使用 等[从 XML 文件中提取数据](它会导致 undesired results

如果您必须使用 shell 脚本,请使用特定于 XML 的命令行工具,例如 XMLStarletxsltproc。如果您还没有安装 XML Starlet,请参考下载信息here


解决方案:

  1. 鉴于您的源 XML 和所需的输出,请考虑使用以下 模板来实现此目的。

    template.xsl

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
      <xsl:output method="text"/>
    
      <xsl:template match="node()|@*">
        <xsl:apply-templates select="node()|@*"/>
      </xsl:template>
    
      <xsl:template match="Module">
        <xsl:choose>
    
          <xsl:when test="@dataPath and not(descendant::*/@dataPath)">
            <xsl:value-of select="@dataPath"/>
            <xsl:text>&#xa;</xsl:text>
          </xsl:when>
    
          <xsl:when test="not(@dataPath) and descendant::*/@dataPath">
            <xsl:for-each select="descendant::*/@dataPath">
              <xsl:value-of select="."/>
              <xsl:if test="position()!=last()">
                <xsl:text>, </xsl:text>
              </xsl:if>
            </xsl:for-each>
            <xsl:text>&#xa;</xsl:text>
          </xsl:when>
    
          <xsl:when test="@dataPath and descendant::*/@dataPath">
            <xsl:value-of select="@dataPath"/>
            <xsl:text>, </xsl:text>
            <xsl:for-each select="descendant::*/@dataPath">
              <xsl:value-of select="."/>
              <xsl:if test="position()!=last()">
                <xsl:text>, </xsl:text>
              </xsl:if>
            </xsl:for-each>
            <xsl:text>&#xa;</xsl:text>
          </xsl:when>
    
        </xsl:choose>
      </xsl:template>
    
    </xsl:stylesheet>
    
  2. 然后运行;

    • 以下 XML Starlet 命令:

      $ xml tr /path/to/template.xsl /path/to/input.xml
      
    • 或以下xsltproc 命令:

      $ xsltproc /path/to/template.xsl /path/to/input.xml
      

    注意:上述命令中template.xslinput.xml 的路径名应重新定义为这些文件所在的位置。

    上述任一命令实质上都会转换您的 input.xml 文件并打印所需的结果。


演示:

  1. 使用以下input.xml 文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
      <Module dataPath="/abc/def/1" handler="DataRegistry" id="id1" path="test.so"/>
    
      <Module id="id2" path="/my/file/path">
        <Config>
          <Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/2" />
          <Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/3" id="V2"/>
        </Config>
      </Module>
    
      <Module id="id3" path="/my/file/path" dataPath="/abc/def/4">
        <Config>
          <Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/5" />
          <Source cutoffpackage="1" dailyStart="20060819" dataPath="/abc/def/6" id="V2"/>
        </Config>
      </Module>
    
      <Module id="id4" path="/my/file/path" dataPath="/abc/def/7"/>
      <Module id="id5" path="/my/file/path" dataPath="/abc/def/8"/>
    
    
      <!-- The following <Module>'s have no associated `dataPath` attribute -->
      <Module id="id6">
        <Config>
          <Source cutoffpackage="1" dailyStart="20060819" id="V2"/>
        </Config>
      </Module>
    
      <Module id="id7"/>
    </root>
    
  2. 然后运行上述任一命令都会打印以下结果:

    /abc/def/1
    /abc/def/2, /abc/def/3
    /abc/def/4, /abc/def/5, /abc/def/6
    /abc/def/7
    /abc/def/8
    

补充说明:

如果您想避免使用单独的 .xsl 文件,您可以在您的 shell 脚本中内联上述 XSLT 模板,如下所示:

script.sh

#!/usr/bin/env bash

xslt() {
cat <<EOX
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text"/>

  <xsl:template match="node()|@*">
    <xsl:apply-templates select="node()|@*"/>
  </xsl:template>

  <xsl:template match="Module">
    <xsl:choose>

      <xsl:when test="@dataPath and not(descendant::*/@dataPath)">
        <xsl:value-of select="@dataPath"/>
        <xsl:text>&#xa;</xsl:text>
      </xsl:when>

      <xsl:when test="not(@dataPath) and descendant::*/@dataPath">
        <xsl:for-each select="descendant::*/@dataPath">
          <xsl:value-of select="."/>
          <xsl:if test="position()!=last()">
            <xsl:text>, </xsl:text>
          </xsl:if>
        </xsl:for-each>
        <xsl:text>&#xa;</xsl:text>
      </xsl:when>

      <xsl:when test="@dataPath and descendant::*/@dataPath">
        <xsl:value-of select="@dataPath"/>
        <xsl:text>, </xsl:text>
        <xsl:for-each select="descendant::*/@dataPath">
          <xsl:value-of select="."/>
          <xsl:if test="position()!=last()">
            <xsl:text>, </xsl:text>
          </xsl:if>
        </xsl:for-each>
        <xsl:text>&#xa;</xsl:text>
      </xsl:when>

    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>
EOX
}

# 1. Using XML Startlet
xml tr <(xslt) /path/to/input.xml

# 2. Or using xsltproc
xsltproc <(xslt) - </path/to/input.xml

注意:您的input.xml 的路径名(即上面script.sh 中的/path/to/input.xml 部分)应再次重新定义为该文件所在的位置。

【讨论】:

    猜你喜欢
    • 2012-11-11
    • 2017-01-02
    • 1970-01-01
    • 2012-01-06
    • 1970-01-01
    • 2012-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多