【问题标题】:How to extract fields which can also contain their delimiters in awk如何在awk中提取也可以包含其分隔符的字段
【发布时间】:2017-08-20 20:00:18
【问题描述】:

我正在尝试从格式如下的文本文件中提取字段值:

{fieldvalue1} {fieldvalue2} {fieldvalue3}

但是,字段值本身可以包含用大括号分隔的子字段,例如:

{abc} {xyz} {efg {123} {pqx}}

所以在上述情况下,所需的输出是:

* fieldvalue1 = abc
* fieldvalue2 = xyz
* fieldvalue3 = efg {123} {pqx}

我尝试了以下过滤器:

sed 's/^{//g;s/}$//g' | awk -F"} {"

但是,这显然无法正确解析上面的 fieldvalue3

【问题讨论】:

  • 这是 JSON 吗?答案是:不要
  • 为什么人们不赞成这个非常明确的问题?我意识到其中有歧义,但我们不需要只是对澄清的努力不屑一顾,不是吗?不,它不是 JSON。
  • 它不是 JSON。它是需要解析的专有外壳的输出。字段名称本身可以包含大括号,并由大括号分隔。
  • 你需要一个理解深度(嵌套结构)并支持递归的解析器;在awk 中实现这一点会很痛苦。

标签: regex bash awk sed


【解决方案1】:

你可以通过计算字符来暴力破解它:

$ cat tst.awk
{
    numFlds = 0
    delete flds
    for (i=1; i<=length($0); i++) {
        char = substr($0,i,1)
        if ( (char == "{") && (++cnt == 1) ) {
            numFlds++
        }
        else if ( (char == "}") && (--cnt == 0) ) {
            # skip it
        }
        else if ( cnt != 0 ) {
            flds[numFlds] = flds[numFlds] char
        }
    }
    for (fldNr=1; fldNr<=numFlds; fldNr++) {
        print fldNr, flds[fldNr]
    }
}

$ awk -f tst.awk file
1 abc
2 xyz
3 efg {123} {pqx}

【讨论】:

    【解决方案2】:

    输入看起来像 tcl list 的列表 :) Tcl 处理得很好。

    在.txt中有逐行读取文件的示例,并在所需的输出中显示字段。

    #!/bin/sh
    # the next line restarts using expect \
        exec tclsh "$0" "$@"
    
    # open file in.txt
    set fd [open in.txt]
    
    # loop till end of file
    while {![eof $fd]} {
        # read line
        set line [gets $fd]
    
        set i 0
        # iterate over all elements
        foreach elm $line {
            incr i
            puts "* fieldvalue$i = $elm"
        }
    }
    close $fd
    

    或者单行示例处理一行数据。 这里使用了expect,因为它允许在命令行中定义tcl命令

     echo '{abc} {xyz} {efg {123} {pqx}}' | expect -c 'puts [join [lmap _ [gets stdin] {incr i; set _ "* fieldvalue$i = $_"}] \n]'
    

    【讨论】:

      【解决方案3】:

      另一个快速 awk:

      #!/usr/bin/awk -f
      
      {
          for(i=1;i<=NF;i++)
          {
              $i = e (e?FS:"") $i
      
              l = split($i,a,"{")
              r = split($i,a,"}")
      
              if(l == r)
              {
                  print "* fieldvalue" ++c,$i
                  e=""
              }
              else
                  e = $i
      
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-01-05
        • 1970-01-01
        • 2020-12-02
        • 1970-01-01
        • 1970-01-01
        • 2014-10-14
        • 1970-01-01
        • 2014-11-06
        • 1970-01-01
        相关资源
        最近更新 更多