【问题标题】:bash: extract an attribute value between quotes using awk or sed(GNU grep is not an option)bash:使用 awk 或 sed 提取引号之间的属性值(GNU grep 不是一个选项)
【发布时间】:2018-04-29 06:28:16
【问题描述】:

我有一个包含如下数据的 xml 文件

<temp>
<a="something" total="50" b="something" total="0" c="something" total="20">
</temp>

我需要得到总计的第一个值,即 50,但我的 sed 解决方案是给出最后一次出现的总计值,即 20

sed -n 's/.*total="\([^"]*\).*/\1/p' temp.xml

输出:20

所需输出:50

感谢您的帮助。谢谢

【问题讨论】:

  • 三个属性同名不是错吗?
  • 贪婪匹配会吃掉最后一个“总数”
  • 您不使用支持 XML 的工具有什么原因吗? //a/@total 将在任何 XPath 处理器中提取您的 50。 (XMLStarlet、非古代 xmllint 等)。
  • ...虽然这实际上不是带有 &lt;a="something" 的有效 XML,但我认为这是错误的总结。
  • 我给出的示例不是有效的 xml。我们的 aix 服务器上没有安装 xml 感知工具

标签: xml bash awk sed aix


【解决方案1】:

您或许应该使用 xml 感知工具,但 grep 可以轻松处理。

grep -Pom1 '(?<=total=")[^"]+' file | head -1
50

这会在第一个匹配的行上查找所有总属性值并获取它们中的第一个。

缺少正则表达式功能,您可以回退到

$ grep -Eo 'total="[^"]+"' file | awk -F\" '{print $2; exit}'

50

【讨论】:

  • OP 说他们不能在 grep 实现中依赖 GNUism,-P 肯定是这样。
【解决方案2】:

sed 不支持非贪婪匹配。摘自here

在 sed 中获得非贪婪匹配的技巧是匹配所有字符,不包括终止匹配的字符。

所以你的解决方案变成了:

sed -n 's/.[^ ]* total="\([^"]*\).*/\1/p' temp.xml

【讨论】:

    【解决方案3】:

    你可以试试

    cut -sd '"' -f 4 my.xml
    

    【讨论】:

    • 这取决于属性的顺序——如果这是真正的 XML(而且看起来确实如此),则不能保证该顺序是稳定的。
    【解决方案4】:

    示例数据文件:

    $ cat my.xml
    <temp>
    <a="something" total="50" b="something" total="0" c="something" total="20">
    </temp>
    

    以及基于有限数据样本和检索第一个 total 值的唯一要求的快速awk 解决方案:

    $ awk -F'"' '/ total=/ { print $4}' my.xml
    50
    
    • -F'"' :将(输入)字段分隔符定义为双引号
    • / total=/ :只对包含字符串“total=”的行感兴趣,然后...
    • print $4 : 打印第四个字段

    【讨论】:

    • 如何检索第一个 total 值,而不是仅检索包含子字符串 total 的行上的第一个值?
    【解决方案5】:

    以下内容将打印文件中所有a 元素的total 属性,逐行打印:

    xmlstarlet sel -t -m '//a[@total]' -v ./@total -n <your-file.xml
    

    如果您没有 XMLStarlet,您可以使用 xsltproc(几乎随处可用)来完成此操作。给定以下 xslt 文件(作为xmlstarlet sel -C -t -m '//a[@total]' -v ./@total -n 的输出生成):

    <?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="//a[@total]">
          <xsl:call-template name="value-of-template">
            <xsl:with-param name="select" select="./@total"/>
          </xsl:call-template>
          <xsl:value-of select="'&#10;'"/>
        </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>
    

    ...你可以运行:

    xsltproc extract-totals.xslt your-file.xml
    

    【讨论】:

      【解决方案6】:

      正确的方法是使用 xml/html 解析器,例如 xmllint 和/或 xmlstarlet

      xmllint解决办法:

      xmllint --html --xpath "string(//a/@total)" temp.xml 2>/dev/null
      50
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-28
        • 2021-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多