在unix中使用jq将表记录转换为JSON文件

在unix中使用jq将表记录转换为JSON文件

我在 Linux 机器上有一个表,其中有许多记录;我正在运行一个查询来获取:

select * from TABNAME_XYZ


CID CN    XY     NAT   UIC    DATE        Region
12  2123  120.9  29.0  100.0  2018-06-08  JAIPUR
13  0987  78.9   100.3 28.8   2020-12-09  DELHI

我想编写一个 shell 脚本,将其输出转换为 JSON,但我真的不知道从哪里开始或做什么。 JSON 必须是这样的:

{"CID":"12","CN":"2123","DATA":{"XY":120.9,"NAT":29.0,"UIC":100.0,"Date":"2018-06-08","REGION":"JAIPUR"}},
{"CID":"13","CN":"0987","DATA":{"XY":78.9,"NAT":100.3,"UIC":28.8,"Date":"2020-12-09","REGION":"DELHI"}}

jq我的系统上已经有了。

答案1

假设字段始终按照给定的顺序,输入的第一行有一个标题行,并且多个空格字符分隔字段,您可以使用 压缩连续空格并使用tr解析数据jq

database-client-command |
tr -s ' ' |
jq -c -Rn '
        input  | split(" ") as $head |
        inputs | split(" ") |
                to_entries |
                        map(.key = $head[.key]) |
                        [ .[:2][], { key: "DATA", value: (.[2:] | from_entries) } ] |
                from_entries'

该表达式从单独的行jq中读取原始数据。tr

第一行被分成标题并存储到 中的数组中$head

我们将剩余的行分成数组,就像处理标题一样。过滤to_entries器将每个数组转换为“条目形式”(带有keyvalue键的对象的集合),并用作为键的map()标头替换数字数组索引。$head

之后map(),过滤器重新排列数组,将第三个元素向下移动到单独的DATA子对象中,并从“条目形式”转换回来。

当完成键和数据重新排列后,from_entries过滤器从“输入表单”返回数组。

脚本的输出将是一组 JSON 对象,并且考虑到问题中的数据,这些对象将如下所示。

{"CID":"12","CN":"2123","DATA":{"XY":"120.9","NAT":"29.0","UIC":"100.0","DATE":"2018-06-08","Region":"JAIPUR"}}
{"CID":"13","CN":"0987","DATA":{"XY":"78.9","NAT":"100.3","UIC":"28.8","DATE":"2020-12-09","Region":"DELHI"}}

如果您想更改RegionREGIONDATEinto Date,请考虑在查询数据库时执行此操作或作为后处理步骤。

请注意,由于第一行尾随逗号,您的预期结果不是有效的 JSON。

答案2

假设您真的不想转换DATEDateRegionREGION(如果您这样做,那么一旦您解释了选择要更改的标签的逻辑,这就是一个简单的调整)并且您确实希望,在每个输出行的末尾除了最后一个(再次强调,如果你不这样做的话,这是一个简单的调整),然后在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ cat tst.awk
NR==1 {
    split($0,tags)
    next
}
{
    printf "%s{%s,%s,\"DATA\":{", sep, fmt(1), fmt(2)
    for (i=3; i<=NF; i++) {
        printf "%s%s", fmt(i), (i<NF ? "," : "}}")
    }
    sep = ",\n"
}
END {
    print ""
}
function fmt(fldNr,     tag, val) {
    tag = tags[fldNr]
    val = $fldNr
    gsub(/"/,"\\\"",val)
    return sprintf("\"%s\":\"%s\"", tag, val)
}

$ awk -f tst.awk file
{"CID":"12","CN":"2123","DATA":{"XY":"120.9","NAT":"29.0","UIC":"100.0","DATE":"2018-06-08","Region":"JAIPUR"}},
{"CID":"13","CN":"0987","DATA":{"XY":"78.9","NAT":"100.3","UIC":"28.8","DATE":"2020-12-09","Region":"DELHI"}}

答案3

这是一种方法。将以下内容另存为foo.awk

{
  if(NR==1){
    for(i=1;i<=NF;i++){
      head[i]=$i
    }
  }
  else{
    printf "{\"%s\":\"%s\",\"%s\":\"%s\",\"DATA\":{\"%s\":%s,\"%s\":%s,\"%s\":%s,\"%s\":\"%s\",\"%s\":\"%s\"}}\n", head[1],$1,head[2],$2,head[3],$3,head[4],$4,head[5],$5,head[6],$6,head[7],$7;

  }
}

进而:

$ cat file |  awk -f foo.awk
{"CID":"12","CN":"2123","DATA":{"XY":120.9,"NAT":29.0,"UIC":100.0,"DATE":"2018-06-08","Region":"JAIPUR"}}
{"CID":"13","CN":"0987","DATA":{"XY":78.9,"NAT":100.3,"UIC":28.8,"DATE":"2020-12-09","Region":"DELHI"}}

在这里,我已将命令的输出保存select在文件中file。在您的情况下,您将使用数据库拥有的任何命令行客户端。例如,使用 mySQL,您将:

mysql -e 'select * from TABNAME_XYZ' | awk -f foo.awk

相关内容