1 介绍
一个灵活的轻量级命令行JSON处理器。
简单讲,就像一个机器,输入JSON数据,经过加工处理后,产出符合你要求的数据,可提取、可转换或其他大量的标准操作。
1.1 功能
- 处理JSON输入,可将给定的过滤器应用与JSON文本输入,并在标准输出上将过滤器的结果生成为JSON
1.2 安装
1
2
3
4
5
|
➜ Debian,如 Ubuntu
sudo apt-get install -y jq
➜ RedHat, 如 CentOS
yum install -y jq
|
1.3 语法
1
2
3
|
jq [options] <jq filter> [file...]
jq [options] --args <jq filter> [strings...]
jq [options] --jsonargs <jq filter> [JSON_TEXTS...]
|
选项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
-c 紧凑而不是漂亮的输出;
-n 使用`null`作为单个输入值;
-e 根据输出设置退出状态代码;
-s 将所有输入读取(吸取)到数组中;应用过滤器;
-r 输出原始字符串,而不是JSON文本;
-R 读取原始字符串,而不是JSON文本;
-C 为JSON着色;
-M 单色(不要为JSON着色);
-S 在输出上排序对象的键;
--tab 使用制表符进行缩进;
--arg a v 将变量$a设置为value<v>;
--argjson a v 将变量$a设置为JSON value<v>;
--slurpfile a f 将变量$a设置为从<f>读取的JSON文本数组;
--rawfile a f 将变量$a设置为包含<f>内容的字符串;
--args 其余参数是字符串参数,而不是文件;
--jsonargs 其余的参数是JSON参数,而不是文件;
-- 终止参数处理;
|
2 示例
以漂亮格式输出
1
2
3
4
5
6
7
8
|
➜ echo '{"foo": {"bar": {"baz": 123}}}' | jq '.'
{
"foo": {
"bar": {
"baz": 123
}
}
}
|
echo ...:首先使用 echo 输出一段 JOSN 格式的字符串。
| jq '.':再使用管道符 | 将 echo ... 的输出结果传到给 jq 处理器。jq 是一个强大的命令行 JSON 处理器,'.' 是一个简单的 jq 表达式,表示原样格式化输出接收的JSON数据。
获取键的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
➜ echo '{"foo": {"bar": {"baz": 123}}}' | jq '.foo'
{
"bar": {
"baz": 123
}
}
➜ echo '{"foo": {"bar": {"baz": 123}}}' | jq '.foo.bar'
{
"baz": 123
}
➜ echo '{"foo": {"bar": {"baz": 123}}}' | jq '.foo.bar.baz'
123
|
jq ".foo":获取JSON字符串中key是 “foo” 的值。
jq '.foo.bar':获取JSON字符串中key是 “bar” 的值。
jq '.foo.bar.baz':获取JSON字符串中key是 “baz” 的值。
数组运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
➜ echo '[{"name": "JSON", "good": true}, {"name": "XML", "good": false}]' | jq '.'
[
{
"name": "JSON",
"good": true
},
{
"name": "XML",
"good": false
}
]
➜ echo '[{"name": "JSON", "good": true}, {"name": "XML", "good": false}]' | jq '.[1]'
{
"name": "XML",
"good": false
}
|
jq '.[1]':输出JSON数组的下标为1的元素。
计算值的长度
1
2
3
|
➜ echo '[{"name": "JSON", "good": true}, {"name": "XML", "good": false}]' | jq '.[] | length'
2
2
|
-
| jq '.[] | length':其中 '.[]' 表示遍历数组中的每个元素;length 表示返回每个元素的长度;
[{"name": "JSON", "good": true}, {"name": "XML", "good": false}] 这一JSON数组包含了两个对象元素,每个对象都有两个属性,分别是name 和 good,所以输出为2(每个对象的属性数量)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
➜ echo '[[1,2], "string", {"a":2}, null]' | jq '.'
[
[
1,
2
],
"string",
{
"a": 2
},
null
]
➜ echo '[[1,2], "string", {"a":2}, null]' | jq '.[] | length'
2
6
1
0
|
- 这一JSON包含四个元素,同样
'.[]' 是遍历数组中的每个元素,对于每个元素调用 length,故第一个元素 [1,2] 的长度是 2(2个元素),"string" 的长度是6(字符串中有6个字符),{"a":2} 的长度是1(对象中有1个属性),null 的长度是 0(null 没有长度)
1
2
|
➜ echo '{"foo": {"bar": {"baz": 123}}}' | jq '.[] | length'
1
|
获取数组的键
1
2
3
4
5
6
7
8
9
10
11
12
13
|
➜ echo '{"abc": 1, "abcd": 2, "Foo": 3}' | jq '.'
{
"abc": 1,
"abcd": 2,
"Foo": 3
}
➜ echo '{"abc": 1, "abcd": 2, "Foo": 3}' | jq 'keys'
[
"Foo",
"abc",
"abcd"
]
|
- 使用关键字
keys 获取数组中的所有键名,无序。
构造一个数组/对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
➜ echo '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' | jq '.'
{
"user": "stedolan",
"titles": [
"JQ Primer",
"More JQ"
]
}
➜ echo '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}' | jq '{user, title: .titles[]}'
{
"user": "stedolan",
"title": "JQ Primer"
}
{
"user": "stedolan",
"title": "More JQ"
}
|
使用多个过滤器
1
2
3
4
5
6
7
8
9
10
|
➜ echo '{ "foo": 42, "bar": "something else", "baz": true}' | jq '.'
{
"foo": 42,
"bar": "something else",
"baz": true
}
➜ echo '{ "foo": 42, "bar": "something else", "baz": true}' | jq '.foo, .bar'
42
"something else"
|
管道
1
2
3
|
➜ echo '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' | jq '.[] | .name'
"JSON"
"XML"
|
- 先遍历数组中的每个元素,然后通过管道将一个过滤器的输出当做下一个过滤器的输入
条件判断
1
2
3
4
5
6
|
➜ echo '[1, 5, 3, 0, 7]' | jq 'map(select(. >= 2))'
[
5,
3,
7
]
|
'map(select(. >= 2))':这是 jq 的一个表达式;map(...) 会对数组中的每个元素应用给定的表达式,并返回一个新的数组。
select(. >= 2):这是一个过滤条件,只选择那些大于或等于 2 的元素。
1
2
|
➜ echo '2' | jq 'if . == 0 then "zero" elif . == 1 then "one" else "many" end'
"many"
|
3 练习
现有 config.json 文件内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
{
"centos7_x64": {
"enable": false,
"count": 5,
"ports": [
"13306:3306",
"18080:18080"
],
"volumes": [
"~/workspace:/root/workspace"
]
},
"centos7_altarch": {
"enable": true,
"count": 5,
"ports":{
"chaos-1": [
"10000:10000",
"18080:18080"
],
"chaos-2": [
"10001:10000",
"18081:18080"
]
},
"volumes": {
"chaos-1": [
"~/workspace:/root/workspace"
],
"chaos-2": [
"~/workspace:/root/workspace"
]
}
}
}
|
现有使用 jq 处理的 Bash 脚本片段如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
jq -c 'to_entries[]' ${CONFIG_FILE} | while read -r entry; do
key=$(echo "${entry}" | jq -r '.key')
value=$(echo "${entry}" | jq -r '.value')
enable=$(echo "${value}" | jq -r '.enable')
template="agent_$(to_lowercase ${key})"
if [[ "${enable}" == "true" ]]; then
# Add template to the file if not already added
if ! contains_element "${template}" "${templates[@]}"; then
echo " ${template}:" >> ${COMPOSE_FILE}
yq eval ".x-templates.${template}" ${TEMPLATES_FILE} | sed 's/^/ /' >> ${COMPOSE_FILE}
templates+=("${template}")
fi
fi
done
|
输出
jq -c 表示以紧凑格式(即不换行)输出数据。类似下面写法:
1
2
|
➜ cat config.json | jq -c '.'
{"centos7_x64":{"enable":false,"count":5,"ports":["13306:3306","18080:18080"],"volumes":["~/workspace:/root/workspace"]},"centos7_altarch":{"enable":true,"count":5,"ports":{"chaos-1":["10000:10000","18080:18080"],"chaos-2":["10001:10000","18081:18080"]},"volumes":{"chaos-1":["~/workspace:/root/workspace"],"chaos-2":["~/workspace:/root/workspace"]}}}
|
to_entries 是 jq 中一个内置函数,用于将JSON对象转换为一个键值对数组。即它将对象的每个键值对转化为一个包含key和value的对象,形成一个数组,好处就是可以使遍历对象的每个属性变得更加简单。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
➜ echo '{"name": "John", "age": 30, "is_student": false}' | jq 'to_entries'
[
{
"key": "name",
"value": "John"
},
{
"key": "age",
"value": 30
},
{
"key": "is_student",
"value": false
}
]
|
结果示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
# jq -c 'to_entries[]' config.json | jq '.'
{
"key": "centos7_x64",
"value": {
"enable": false,
"count": 5,
"ports": [
"13306:3306",
"18080:18080"
],
"volumes": [
"~/workspace:/root/workspace"
]
}
}
{
"key": "centos7_altarch",
"value": {
"enable": true,
"count": 5,
"ports": {
"chaos-1": [
"10000:10000",
"18080:18080"
],
"chaos-2": [
"10001:10000",
"18081:18080"
]
},
"volumes": {
"chaos-1": [
"~/workspace:/root/workspace"
],
"chaos-2": [
"~/workspace:/root/workspace"
]
}
}
}
|
| while read -r entry; do 通过管道将 jq 的输出传递到 while 循环中,一次读取一个键值对,存储在 entry 变量中。
key=$(echo "${entry}" | jq -r '.key') 使用 jq -r 从 entry 中提取 key 值,-r 表示以原始格式输出,不加引号。
value=$(echo "${entry}" | jq -r '.value') 从 entry 中提取 value 值。
enable=$(echo "${value}" | jq -r '.enable') 从 value 中提取 enable 字段,判断该功能是否启用。
4 参考