我在 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
器将每个数组转换为“条目形式”(带有key
和value
键的对象的集合),并用作为键的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"}}
如果您想更改Region
为REGION
和DATE
into Date
,请考虑在查询数据库时执行此操作或作为后处理步骤。
请注意,由于第一行尾随逗号,您的预期结果不是有效的 JSON。
答案2
假设您真的不想转换DATE
为Date
和Region
到REGION
(如果您这样做,那么一旦您解释了选择要更改的标签的逻辑,这就是一个简单的调整)并且您确实希望,
在每个输出行的末尾除了最后一个(再次强调,如果你不这样做的话,这是一个简单的调整),然后在每个 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