【问题标题】:Extract values of attribute from a line in file从文件中的一行提取属性值
【发布时间】:2018-05-02 12:37:58
【问题描述】:

我有一个包含多个属性的大单行(只有一行)文件。文件类似于 XML,但不是有效的 XML,因此不确定是否可以使用 XML 实用程序。该行如下所示:

<a="1" b="2" time="10" c="3" time="1" time="3" d="1" e="1" f="1" time="10" .... />

我想提取所有时间值并总结。 time 可以是队列中的任何位置。 GNU grep 不可用,因此无法使用 grep -oP

有人可以帮我处理 bash 脚本/命令吗?

【问题讨论】:

  • 您有xsltproc 可用吗?如果是这样,您可以通过将内容正确解析为 XML 来做到这一点,而不是尝试将其解释为文本。

标签: bash shell unix aix


【解决方案1】:

您可以使用 Perl 提取 time="..." 中的数字并将它们相加:

perl -pne 'use List::Util qw(reduce); $_ = reduce { $a + $b } /time="(\d+)"/g' < file

诀窍在于 Perl 中的 /.../ 运算符返回捕获组中的值列表。 在此示例中,time="..." 中的值。

List::Util::reduce 是计算它们的总和。

-ne是将stdin的内容读入$_变量, 而-p 是自动打印$_ 变量的值(我们将其替换为值的总和)。

【讨论】:

    【解决方案2】:

    awk怎么样?

    awk 'BEGIN {RS=" "; FS="\""; sum=0} /time*/{sum+=$2} END {print sum}' data.xml
    

    解释:

    • RS 代表记录分隔符,因此我们要求awk 一次处理一对key=value
    • FS代表字段分隔符,所以我们用"分割记录,这样便于数值的提取。
    • /time*/{sum+=$2} 将匹配以 time 开头的任何记录,并将该值添加到我们的全局 sum
    • 在程序结束时,我们只打印在sum 中累积的值。

    【讨论】:

      【解决方案3】:

      这里不需要 GNU grep 的 -P

      grep -o 'time="[^"]*"' infile | cut -d '"' -f2 | paste -s -d+ | bc
      

      这会提取所有time="dd" 字符串:

      time="10"
      time="1"
      time="3"
      time="10"
      

      cut" 为分隔符然后提取第二个字段,即数字:

      10
      1
      3
      10
      

      paste -s -d+ 将所有内容放回一行,使用+ 作为分隔符:

      10+1+3+10
      

      bc 计算结果:

      21
      

      如果您的 grep 甚至没有 -o 选项,我刚刚意识到它既不在 POSIX 也不在 AIX grep 中,您可以执行类似的操作

      tr ' ' '\n' < infile | grep 'time=' | cut -d '"' -f2 | paste -s -d+ | bc
      

      首先将每个键/值对放在单独的行上。这应该适用于具有这些实用程序的符合 POSIX 版本的任何系统。

      【讨论】:

      • 我喜欢这个管道,虽然我从来都不是 bc 整数数学的粉丝。
      • @DavidC.Rankin 也可以在没有 bc 的情况下使用 echo $((&lt;pipeline&gt;)),但是对于整数数学,您不喜欢 bc 什么?
      • 唯一不喜欢整数数学的是 $((...)) 已经是 POSIX 的一部分。 bccalc(我真的很喜欢 calc)适用于浮点数。您可以将它用于整数,但它只需要一个额外的管道并调用bc(如果您在循环中执行计算,这将变得很昂贵)。至少这是我的思考过程(它可能在杂草丛生或有点两极......)
      【解决方案4】:

      有趣的总结。您也可以单独使用 grep -ow 和 bash 来完成。注意:要使其与 POSIX shell 兼容,您需要将grep 表达式的输出通过管道传输到read,而不是使用进程替换。 (并使用sum="${sum}+$n" 而不是+= 变体)您可以执行以下操作:

      $ c=0; sum=; \
      while read -r n; do \
          ((c > 0)) && sum+="+$n" || sum="$n"; ((c++)); \
      done < <(grep -ow "[0-9]*" yourfile); \
      echo $((sum)))
      33
      

      将其添加到组合中。如果你理解了所有的答案,你就会有更多的工具可以添加到你的 shell 工具箱中。

      输入文件

      $ cat yourfile
      a="1" b="2" time="10" c="3" time="1" time="3" d="1" e="1" f="1" time="10"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-12-27
        • 1970-01-01
        • 1970-01-01
        • 2010-11-21
        • 1970-01-01
        • 2019-01-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多