Shell脚本中解析json
由于工作需要,需要在Shell脚本中解析json文件。
安装软件
这里需要使用开源jq命令,首先从官方下载最新版jq
https://stedolan.github.io/jq/download/ 例如https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
下载之后将jq-linux64重命名为jq
$ mv jq-linux64 jq
然后执行./jq即可查看帮助
最简单的示例
$ echo '{"foo": 0,"bar":1}' | ./jq .
{
"foo": 0,
"bar": 1
}
注意jq后面的点号,其表示格式化输出json串。还可以直接处理已有的文件,如下
$cat jsonData.josn
{"productId":"2723","click":60,"view":300,"deal":2,"day":"20130919"}
{"productId":"2728","click":130,"view":800,"deal":10,"day":"20130919"}
{"productId":"3609","click":50,"view":400,"deal":3,"day":"20130919"}
$./jq -r . jsonData.josn
{
"productId": "2723",
"click": 60,
"view": 300,
"deal": 2,
"day": "20130919"
}
{
"productId": "2728",
"click": 130,
"view": 800,
"deal": 10,
"day": "20130919"
}
...
示例
提取json字段
例如我们需要提取所有的productId及其click,那么我们就可以这样:
./jq -r '.productId +","+(.click|tostring)' jsonData.josn
2723,60
2728,130
3609,50
需要注意的是,由于click的值是数值,其不能直接和字符串进行拼接,所以我们对其调用了tostring的方法,语法为(.click|tostring);
然后我们通过+来将两个字段以及逗号进行了拼接;
另外注意每个属性名前的点号(.productId),点号表示根节点
访问数组
考虑如下json
{
"tasks": {
"task": [
{
"id": "task_1550858682031_448878_m_000869",
"startTime": 1551150749829,
"finishTime": 1551150766484
},
{
"id": "task_1550858682031_448878_m_001389",
"startTime": 1551150904151,
"finishTime": 1551150920778
}
]
}
}
task是一个数组,怎么将所有task的id及startTime,finishTime以csv的格式输出呢,如下:
第一步,遍历数组
./jq -r '.tasks.task[]' task.json
{
"id": "task_1550858682031_448878_m_000869",
"startTime": 1551150749829,
"finishTime": 1551150766484
}
{
"id": "task_1550858682031_448878_m_001389",
"startTime": 1551150904151,
"finishTime": 1551150920778
}
可以看到,数组是通过[]来访问的,[]表示遍历整个数组,如果你只想访问数组中的第2个元素(下标从0开始),你可以使用[1];如果想要访问第3个到第5个元素,可以使用[2:4]
第二步,访问每个元素的属性
./jq -r '.tasks.task[].id,.tasks.task[].startTime,.tasks.task[].finishTime' task.json
task_1550858682031_448878_m_000869
task_1550858682031_448878_m_001389
1551150749829
1551150904151
1551150766484
1551150920778
怎么没有按列输出,而是按行输出了呢?这是因为如果表达式被逗号分隔,则表示对进同一输入应用两次表达式,产生多行输出。那如果我想在一行中输出应该怎么办呢?当然是只编写一个表达式,怎么只编写一个表达式呢?
答案是使用管道符|,如下
./jq -r '.tasks.task[] | .id +","+(.startTime|tostring)+","+(.finishTime|tostring)' task.json
task_1550858682031_448878_m_000869,1551150749829,1551150766484
task_1550858682031_448878_m_001389,1551150904151,1551150920778
定义数组
除了上面的使用+来进行字符串拼接,我们还可以使用[]来定义数组,然后直接输出数组,更简洁
./jq -r '[.productId,.click]' jsonData.josn
[
"2723",
60
]
[
"2728",
130
]
[
"3609",
50
]
还可以调用数组的join方法,来将数组转换成拼接字符串,如下
./jq -r '([.productId,.click])|join(",")' jsonData.josn
2723,60
2728,130
3609,50
输出格式
除了使用前面字符串拼接的方式来输出csv格式之外,我们还可以使用预置的格式指令,例如@csv
./jq -r '.tasks.task[] | [.id,.startTime,.finishTime] | @csv' task.json
"task_1550858682031_448878_m_000869",1551150749829,1551150766484
"task_1550858682031_448878_m_001389",1551150904151,1551150920778
因为@csv的输入必须是数组,所以我们使用[.id,.startTime,.finishTime]来构造了数组
更多其它使用方法请参考如下链接
官方文档:https://stedolan.github.io/jq/manual
表达式在线测试器:https://jqplay.org/