linux / bash 解析类似 json 的数据

linux / bash parse through json-like data

这是我的一些数据:

animal { 
    dog {
        body {
            parts {
                legs = old
                brain = average
                tail= curly
                }
   
            }
        }
    cat {
        body {
            parts {
                legs = new
                brain = average
                tail {
                    base=hairy
                    tip=nothairy
                }
   
            }
        }
    }
}

注意数据并不是真正的 json 因为它有以下规则:

甚至可以用 awk 或 sed 解析它吗?我试过 jq 但它不起作用,因为这不是真的 json 数据。

我的目标是只显示“狗”和“猫”。基于它们是“动物”下的最高值。

$ some-magical-command
dog
cat

如果您想学习一门新语言,相当 接近 语法。

set data {
    animal { 
        dog {
            body {
                parts {
                    legs = old
                    brain = large
                    tail= curly
                    }
       
                }
            }
        cat {
            body {
                parts {
                    legs = new
                    brain = tiny
                    tail {
                        base=hairy
                        tip=nothairy
                    }
       
                }
            }
        }
    }
}

set data [regsub -line -all {\s*=\s*(.+)} $data { ""}]

dict get $data animal dog body parts brain    ;# => large

我知道有些人会争论你对狗脑和猫脑的分类...

如果您只需要 second-level 键,并且您不太关心为错误输入生成良好的错误消息,那么它很漂亮 straight-forward。基本思路是这样的:

  1. 输入行的三种格式:

    • ID {
    • ID = 值 # 其中 = 可能不是 space-separated
    • }
  2. 读取行时,我们通过使用第一种行类型递增计数器并使用第三种行类型递减来跟踪嵌套深度。

  3. 当嵌套计数器为1时,如果该行有ID字段,我们打印它。

这可以通过 awk 脚本非常简单地完成。此脚本应保存在名称类似于 level2_keys.awk 的文件中;然后你可以执行命令awk -f level2_keys.awk /path/to/input/file。请注意,所有规则都以 next; 结尾,以避免在评估匹配后出现规则。

 == "}"    { # Decrement nesting on close
               --nesting;
               next;
             }
/=/          { # Remove the if block if you don't want to print these keys.
               if (nesting == 1) {
                 gsub("=", " = ");    # Force = to be a field
                 print();
               }
               next;
             }
 == "{"    { # Increment nesting (and maybe print) on open
               if (nesting == 1) print();
               ++nesting;
               next;
             }
# NF is non-zero if the line is not blank.
NF           { print "Bad input at " NR ": '"[=10=]"'" > "/dev/stderr"; }

为了做你现在想做的事并为了将来对你的数据进行任何操作,你可以使用任何 POSIX awk(对于字符 类)将你的结构转换为JSON 然后在上面使用 jq:

$ cat tst.awk
BEGIN { print "{" }
!NF { next }
{
    sub(/[[:space:]]+$/,"")
    gsub(/[[:alnum:]_]+/,"\"&\"")
    gsub(/ *= */,": ")
    sub(/" *{/,"\": {")
}
(++nr) > 1 {
    sep = ( /"/ && (prev ~ /["}]$/) ? "," : "" )
    printf "%s%s%s", prev, sep, ORS
}
{ prev = [=10=] }
END { print prev ORS "}" }

$ awk -f tst.awk file
{
"animal": {
    "dog": {
        "body": {
            "parts": {
                "legs": "old",
                "brain": "average",
                "tail": "curly"
                }
            }
        },
    "cat": {
        "body": {
            "parts": {
                "legs": "new",
                "brain": "average",
                "tail": {
                    "base": "hairy",
                    "tip": "nothairy"
                }
            }
        }
    }
}
}

当前和一些未来可能的用途:

$ awk -f tst.awk file | jq -r '.animal | keys[]'
cat
dog

$ awk -f tst.awk file | jq -r '.animal.dog.body.parts | keys[]'
brain
legs
tail

$ awk -f tst.awk file | jq -r '.animal.dog.body.parts'
{
  "legs": "old",
  "brain": "average",
  "tail": "curly"
}

$ awk -f tst.awk file | jq -r '.animal.cat.body.parts'
{
  "legs": "new",
  "brain": "average",
  "tail": {
    "base": "hairy",
    "tip": "nothairy"
  }
}

以上假定您的输入始终如您的问题所示。