我想将数据库中的 sqlite 数据转换为 JSON 格式。
我想使用以下语法:
sqlite3 -line Members.db“SELECT * FROM Members LIMIT 3”>members.txt
输出:
id = 1
fname = Leif
gname = Håkansson
genderid = 1
id = 2
fname = Yvonne
gname = Bergman
genderid = 2
id = 3
fname = Roger
gname = Sjöberg
genderid = 1
如何在 for 循环中使用漂亮且结构化的代码来做到这一点? (仅在 Bash 中)
我尝试过一些 awk 和 grep,但还没有取得很大的成功。
如果有一些建议就好了。
我想要类似这样的结果:
[
{
"id":1,
"fname":"Leif",
"gname":"Hakansson",
"genderid":1
},
{
"id":2,
"fname":"Yvonne",
"gname":"Bergman",
"genderid":2
},
{
"id":3,
"fname":"Roger",
"gname":"Sjberg",
"genderid":1
}
}
如果你的sqlite3是用json1扩展编译的(或者如果你可以获得带有json1扩展的sqlite3版本),那么你可以用它来生成JSON对象(每行一个JSON对象)。 例如:
select json_object('id', id, 'fname', fname, 'gname', gname, 'genderid', genderid) ...
然后您可以使用jq等工具将对象流转换为对象数组,例如将 sqlite3 的输出通过管道传输到
jq -s .
。
(一个不太烦人的替代方案可能是使用 sqlite3 函数 json_array(),它会生成一个数组,您可以使用 jq 将其重新组装成一个对象。)
如果 json1 扩展不可用,那么您可以使用以下内容作为起点:
awk 'BEGIN { print "["; }
function out() {if (n++) {print ","}; if (line) {print "{" line "}"}; line="";}
function trim(x) { sub(/^ */, "", x); sub(/ *$/, "", x); return x; }
NF==0 { out(); next};
{if (line) {line = line ", " }
i=index($0,"=");
line = line "\"" trim(substr($0,1,i-1)) ": \"" substr($0, i+2) "\""}
END {out(); print "]"} '
或者,您可以使用以下 jq 脚本,它将“=”右侧出现的数字字符串转换为数字:
def trim: sub("^ *"; "") | sub(" *$"; "");
def keyvalue: index("=") as $i
| {(.[0:$i] | trim): (.[$i+2:] | (tonumber? // .))};
[foreach (inputs, "") as $line ({object: false, seed: {} };
if ($line|trim) == "" then { object: .seed, seed : {} }
else {object: false,
seed: (.seed + ($line | keyvalue)) }
end;
.object | if . and (. != {}) then . else empty end ) ]
只需使用 SQLite 3.33.0 或更高版本输入
-json
参数即可获得 json 输出:
$ sqlite3 -json database.db "select * from TABLE_NAME"
来自 SQLite 版本 3.33.0 注意:
...
- CLI 增强功能:
- 添加了四种新的输出模式:“box”、“json”、“markdown”和“table”。
- “列”输出模式自动扩展列以包含最长的输出行,并自动打开“.header”(如果有) 之前没有设置过。
- “引用”输出模式尊重“.separator”
- CLI 内置了十进制扩展和 ieee754 扩展
...
我想我更喜欢用每条记录一行来解析 sqlite 输出,而不是您使用
sqlite3 -line
建议的非常冗长的输出格式。所以,我会这样:
sqlite3 members.db "SELECT * FROM members LIMIT 3"
这给了我这个解析:
1|Leif|Hakansson|1
2|Yvonne|Bergman|2
3|Roger|Sjoberg|1
如果我将输入分隔符设置为
awk
和 ,我现在可以用
|
解析它
awk -F '|'
并使用以下内容选取每行上的 4 个字段,并将它们保存在数组中,如下所示:
{ id[++i]=$1; fname[i]=$2; gname[i]=$3; genderid[i]=$4 }
然后我需要做的就是在最后打印出你需要的输出格式。然而,你的输出中有双引号,并且在
awk
中引用它们很痛苦,所以我暂时使用另一个管道符号 (|
) 作为双引号,然后,在最后,我得到 tr
用双引号替换所有管道符号 - 只是为了使代码更容易阅读。所以总体解决方案如下所示:
sqlite3 members.db "SELECT * FROM members LIMIT 3" | awk -F'|' '
# sqlite output line - pick up fields and store in arrays
{ id[++i]=$1; fname[i]=$2; gname[i]=$3; genderid[i]=$4 }
END {
printf "[\n";
for(j=1;j<=i;j++){
printf " {\n"
printf " |id|:%d,\n",id[j]
printf " |fname|:|%s|,\n",fname[j]
printf " |gname|:|%s|,\n",gname[j]
printf " |genderid|:%d\n",genderid[j]
closing=" },\n"
if(j==i){closing=" }\n"}
printf closing;
}
printf "]\n";
}' | tr '|' '"'
Sqlite-utils 正是您所寻找的。默认情况下,输出将为 JSON。
迟插总比不插好。
将 sqlite3 保存到文本文件。
获取 jo(jo 也可以在发行版存储库中找到)
并使用这个 bash 脚本。
while read line
do
id=`echo $line | cut -d"|" -f1`
fname=`echo $line | cut -d"|" -f2`
gname=`echo $line | cut -d"|" -f3`
genderid=`echo $line | cut -d"|" -f4`
jsonline=`jo id="$id" fname="$fname" gname="$gname" genderid="$genderid"`
json="$json $jsonline"
done < "$1"
jo -a $json
请不要使用
awk
创建(或解析)json。有专门的工具可以做到这一点。像xidel这样的工具。xidel
也可以将纯文本转换为 JSON:
$ xidel -s members.txt -e '
array{
for $member in tokenize($raw,"\n\n") return
map:merge(
for $entry in x:lines($member)
let $val:=tokenize($entry," = ")
return {
normalize-space($val[1]):$val[2]
}
)
}
'
[
{
"id": "1",
"fname": "Leif",
"gname": "Håkansson",
"genderid": "1"
},
{
"id": "2",
"fname": "Yvonne",
"gname": "Bergman",
"genderid": "2"
},
{
"id": "3",
"fname": "Roger",
"gname": "Sjöberg",
"genderid": "1"
}
]
x:lines()
是 tokenize(.,'\r\n?|\n')
) 的简写