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
如果您想学习一门新语言,相当 接近 tcl 语法。
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。基本思路是这样的:
输入行的三种格式:
- ID {
- ID = 值 # 其中 = 可能不是 space-separated
- }
读取行时,我们通过使用第一种行类型递增计数器并使用第三种行类型递减来跟踪嵌套深度。
当嵌套计数器为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"
}
}
以上假定您的输入始终如您的问题所示。
这是我的一些数据:
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
如果您想学习一门新语言,相当 接近 tcl 语法。
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。基本思路是这样的:
输入行的三种格式:
- ID {
- ID = 值 # 其中 = 可能不是 space-separated
- }
读取行时,我们通过使用第一种行类型递增计数器并使用第三种行类型递减来跟踪嵌套深度。
当嵌套计数器为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"
}
}
以上假定您的输入始终如您的问题所示。