推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

Linux shell json解析工具——jq

   2023-07-24 网络整理佚名1700
核心提示:json格式是数据传输过程中一种通用的格式,对于而言,由于有多种json包,解析json并不是什么难事。shell中,同样有一种强大的json解析工具——jq。通过内置函数提供了数据处理时常用的操作,例如:过滤,映射,路径操作等。提供了一些特殊的语法和内置函数用来缩减迭代器运算结果的个数。除了在表达式内部定义函数外,我们可以把自定义函数写在外部文件中形成单独的类库。

json格式是数据传输过程中常见的格式。 对于,由于json包很多,所以解析json并不困难。

在Linux shell中,还有一个强大的json解析工具——jq。 jq没有依赖关系,只有一个二进制文件。

安装

官方网站:

使用

命令格式

1

jq [options...] filter [files...]

可以使用以下格式

1
2
3
4
5

jq -c '' a.json
jq '' a.json

jq -c '.foo' a.json
cat a.json | jq -c '.foo'

参数说明:

- / -C

默认情况下,jq会将json格式化为多行树形结构输出,但有时需要一行输出json字符串,可以使用该参数

过滤器说明

jq通过命令行选项控制输入输出的处理。几个重要的选项如下

JSON

以下面的字符串为例

1
2

# cat json_raw.txt
{"routeTableId":"rt-kjjhbqkw8dk3","vpcId":"vpc-ydbn98nzsw67","description":{"time_zone":"shanghai"},"routeRules":[{"routeRuleId":"rr-psj2t3mbqhmq","sourceAddress":"192.168.0.0/20","destinationAddress":"0.0.0.0/0","nexthopId":"nat-byqr8xwnbh2e","nexthopType":"nat","description":"","pathType":"normal","routeTableId":"rt-kjjhbqkw8dk3"},{"routeRuleId":"","sourceAddress":"0.0.0.0/0","destinationAddress":"192.168.0.0/20","nexthopId":"","nexthopType":"sys","description":"","routeTableId":"rt-kjjhbqkw8dk3"}]}

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

# cat json_raw.txt | jq .
{
"routeTableId":"rt-kjjhbqkw8dk3",
"vpcId":"vpc-ydbn98nzsw67",
"description":{
"time_zone":"shanghai"
},
"routeRules":[
{
"routeRuleId":"rr-psj2t3mbqhmq",
"sourceAddress":"192.168.0.0/20",
"destinationAddress":"0.0.0.0/0",
"nexthopId":"nat-byqr8xwnbh2e",
"nexthopType":"nat",
"description":"",
"pathType":"normal",
"routeTableId":"rt-kjjhbqkw8dk3"
},
{
"routeRuleId":"",
"sourceAddress":"0.0.0.0/0",
"destinationAddress":"192.168.0.0/20",
"nexthopId":"",
"nexthopType":"sys",
"description":"",
"routeTableId":"rt-kjjhbqkw8dk3"
}
]
}

JSON解析器

根据key获取value

1
2
3
4
5
6

# cat json_raw.txt | jq ".vpcId"
"vpc-ydbn98nzsw67"

# cat json_raw.txt | jq ".vpcId,.routeTableId"
"vpc-ydbn98nzsw67"
"rt-kjjhbqkw8dk3"

当key不存在时返回null

1
2

# cat json_raw.txt | jq ".test"
null

JSON解析器

1
2

# cat json_raw.txt | jq ".description.time_zone"
"shanghai"

JSON解析数组

1
2
3
4
5
6

# cat json_raw.txt | jq ".routeRules[].sourceAddress"
"192.168.0.0/20"
"0.0.0.0/0"

# cat json_raw.txt | jq ".routeRules[1].sourceAddress"
"0.0.0.0/0"

内置功能

jq还有一些内置函数,例如keys和has。

keys用于获取JSON中的关键元素

1
2
3
4
5
6
7

# cat json_raw.txt | jq 'keys'
[
"description",
"routeRules",
"routeTableId",
"vpcId"
]

has用于判断某个key是否存在

1
2
3
4
5

# cat json_raw.txt | jq 'has("description")'
true

# cat json_raw.txt | jq 'has("test")'
false

其他用途

使用jq的管道提取特定字段

1
2
3
4
5
6
7

$ cat test.json | jq ".LoadBalancers.LoadBalancer | .[0].LoadBalancerId"
"123"


$ cat test.json | jq ".LoadBalancers.LoadBalancer[0].LoadBalancerId"
"123"

自定义输出字段名,将获取到的字符串拼接成新的json:

1
2
3
4
5

$ cat test.json | jq ".LoadBalancers.LoadBalancer | {"id1" : .[0].LoadBalancerId, name1: .[0].LoadBalancerName}"
{
"name1": "1.test.com",
"id1": "123"
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

$ cat test.json | jq "[.LoadBalancers.LoadBalancer[] | {"id" : .LoadBalancerId, name: .LoadBalancerName}]"
[
{
"name":"1.test.com",
"id":"123"
},
{
"name":"2.test.com",
"id":"456"
},
{
"name":"3.test.com",
"id":"789"
}
]

查看元素数量

1
2

$ cat test.json | jq ".LoadBalancers | length"
1

jq功能超级强大,甚至包括索引、add、逻辑判断、正则表达式、debug、try-catch、while、split等,详细请参考。

基础表达

基本表达式(Basic)是jq提供的基本过滤器,用于访问JSON对象中的属性。 基本表达式也是实现更复杂查询功能的基础。 基本表达方式主要包括以下几种:

表达式操作',' 和 '|')。 表达式操作用于关联多个基本表达式。 其中逗号表示将多个表达式应用于同一输入。 管道符号的意思是使用前一个表达式的输出作为下一个表达式的输入。 当前一个表达式的结果是迭代器时,迭代器中的每个值都将用作下一个表达式的输入,以形成新的表达式。 例如“.[]|.+1”,在此表达式中,第一个子表达式“.[]”在输入数组上构建迭代器,第二个子表达式将迭代器的每个元素加 1。内置算术支持

jq支持的数据类型有:数字、字符串、数组和对象()。 并且在这些数据类型的基础上,jq提供了一些基本的运算符来实现一些基本的运算和数据操作。 下面列出:

jq中有一个非常特殊的运算规则:当一个运算符的一个或两个操作数为迭代器时,其运算的方式类似于笛卡尔积,即将两个操作数的每个元素分别取出来进行运算。 例如:

1
2

#result is 5 6 7 8
jq -n '([1,2]|.[])+([4,6]|.[])'

jq内部支持两种控制结构:判断语句和异常处理。

判断语句的完整结构是if then-elif then-else-end。 当判断条件的结果为多个值(迭代器)时,会对每个值进行一次判断。

异常处理语句的结构是try catch。 当表达式a发生异常时,执行表达式b,输入的是捕获的异常信息。 如果不需要额外的处理,只是为了简单的抑制异常信息的输入,可以不存在catch语句(如try.a)。 此时整个表达式可以简写为‘?’ (例如:.a?)。

jq 内部也支持函数。 使用jq函数时,要注意区分两个概念:输入和参数。 输入可以是整个表达式的输入数据,也可以是表达式其他部分的输出。 参数和函数一起形成新的过程输入。 与其他编程语言不同,调用函数时,多个参数之间用分号分隔。 jq通过内置函数提供数据处理中的常用操作,如过滤、映射、路径操作等。

地图操作

在数据处理的过程中,我们经常需要将数据从一种形式转换为另一种形式,或者改变数据的值。 jq 提供了两个内置映射函数来实现这种转换:map 和 . 其中,map处理的对象是一个数组,处理对象属性的值。 map 函数的参数是一个表达式。 在这个表达式中,“.” 表示被映射的元素或值。

1
2
3

#输入:[1,2,3,4]
jq 表达式:jq -r 'map(.+1)'
#输出:[2,3,4,5]

过滤操作

jq中有两种选择过滤操作。 第一种是根据数据类型进行过滤,例如表达式'.[]|'的结果仅包含数组。 可用于过滤的过滤器类型有: , , , , , , , , nulls, , 。

第二种是函数。 接受条件表达式作为参数。 它的输入可以是迭代器,也可以与map函数一起使用来处理数组。 当输入中的某个元素使参数中的条件表达式计算结果为 true 时,该元素将保留在结果中,否则不会。

1
2
3
4
5

#输入:[1,2,3,4]
jq -r 'map(select(.>2))'
#输出:[3,4]
jq -r '.[]|select(.>2)'
#输出:3 4

路径操作

与xpath类似,jq中的path也指从根到某个叶子属性的访问路径。 jq中有两种表达路径的方式:数组表示法和属性表示法。 属性表示法类似于我们如何访问 中的某个属性值,例如“.ab”。 数组表示法将路径的每个部分表示为数组的元素。 jq提供了一个内置函数path来实现path从属性表示法到数组表示法的转换。

jq还提供了读取路径值()、设置路径值()和删除路径(del)的函数。 不幸的是,这三个函数不能一致地处理路径。 where 和 仅接受数组表示法中的路径,并且 del 函数仅正确处理属性表示法中的路径。

jq还提供了一个函数paths用于枚举可能的路径。 如果不带参数,paths 函数将输出 JSON 数据中所有可能的路径。 paths 函数可以接受过滤器,仅输出满足条件的路径。

存在判断函数

jq 提供了一系列函数来确定输入数据中是否存在某个元素或属性。 其中,函数has和in用于判断JSON对象或数组是否包含特定属性或索引。 函数sum用于判断参数是否完全包含在输入数据中。 对于不同的数据类型,判断是否完全包含的规则是不同的。 对于字符串,如果 A 是 B 的子字符串,则认为 A 完全包含在 B 中。对于对象类型,如果在对象 B 中找到具有相同值的对象 A 的所有属性,则认为对象 A 完全包含在 B 中。

数组函数

除了上面介绍的基本运算符之外,jq 还提供了内置函数,用于数组的展平 ()、反转 ()、排序 (sort, )、比较 (min,, max,) 和搜索 (, index and)。 函数的输入数据可以是数组或字符串。 与索引函数不同的是,结果是一个数组,包含所有参数在输入数据中的位置,具体请参考以下示例。

1
2
3
4
5
6
7
8
9

#结果是[1,2,3,4]
jq -nr '[1,[2,3],4]|flatten'
#结果是[3,2,1]
jq -nr '[1,2,3]|reverse'
jq -nr '[3,1,2]|sort'
jq -nr '[{"a":1},{"a":2}]|sort_by(.a)'
#下面两个表达式的结果都是[1,3]
jq -nr '"abcb"|indices("b")'
jq -nr '[1,3,2,3]|indices(3)'

jq还提供了许多其他内置函数,详细信息请参阅jq在线文档。

jq高级特征变量

jq内部支持两种定义变量的方式。 第一个,正如我们之前在 jq 的调用部分提到的,可以通过命令行参数(-arg)来定义。 该方法用于从外部(例如:shell)传入数据以在表达式中使用。

第二种方式,在 jq 表达式中,我们可以自己声明一个变量来保存表达式的结果以供表达式的其余部分使用。

jq中定义变量的语句为:as $

定义和使用变量

1
2
3
4

#在下面的表达式中变量$arraylen 用来保存数组长度,整个表达式结果为 4
jq -nr '[1,2,3]|length as $arraylen|$arraylen+1'
#可以同时定义多个变量
jq -nr '{"firstname":"tom","lastname":"clancy"}|. as {firstname:$fn, lastname:$ln}|"author is "+$fn+"*"+$ln'

jq中还存在变量作用域问题。 在jq中,有两种方法来分隔变量作用域。 第一种是用括号将表达式的一部分括起来。 括号内的表达式与括号外的表达式不在同一范围内。 第二种方法是定义函数。 默认情况下,声明的变量对后面的表达式可见。 但是,如果变量在特定作用域内声明,则该变量对于作用域外的表达式不可见,例如:

变量范围

1
2
3
4
5
6
7
8

#会抛出 arraylen 没定义的异常
jq -nr '[1,2,3]|(length as $arraylen|$arraylen)|$arraylen+1'

#正常执行,结果为 4.
jq -nr '[1,2,3]|(length as $arraylen|$arraylen+1)'

#函数作用域。该表达式会抛出异常,因为变量$fn 是在函数 fname 中定义,对最后一个子表达式##来说,$fn 是不可见的。
jq -nr '{"firstname":"tom","lastname":"clancy"}|def fname:. as {firstname:$fn, lastname:$ln}|$fn; fname|$fn'

我们知道jq有一个特殊的数据类型:迭代器。 通常,涉及迭代器的操作的结果也是迭代器。 jq 提供了一些特殊的语法和内置函数来减少迭代器结果的数量。

关键字用于通过运算将迭代器的所有值组合成一个值。 其调用形式为:如$var(INIT;)。 其中,表达式itep生成的迭代器被赋值给变量var,它是关于变量var的表达式。 INIT 是该表达式的初始输入。 对于 itexp 结果中的每个元素调用一次表达式,并将结果用作下一次调用的输入。

关键词

1
2
3
4

#结果是 6
jq -nr 'reduce ([1,2,3]|.[]) as $item (0; .+$item)'
#上面的表达式等同于
jq -nr '0 | (3 as $item|.+$item)|(2 as $item | . + $item)|(1 as $item | . + $item)'

关键字的工作原理类似。 其调用形式为 EXP as $var (INIT; ; )。 与关键字不同的是,关键字的每次迭代都是先调用后调用,并用一个迭代器来保存每次的中间结果。 该迭代器最终作为整个表达式的结果输出。

关键词

1
2

#下面的表达式,结果是 1 3 6
jq -nr 'foreach ([1,2,3]|.[]) as $item (0; .+$item;.)'

内置函数limit(n;exp)用于获取表达式exp结果的前n个值。

内置函数first、last 和nth。 这些函数用于获取迭代器中的特定元素。 这些函数可以作为函数或子表达式来调用。 看一下下面的例子:

第一个、最后一个和第 n 个

1
2
3
4
5
6
7

#下面的表达式按照函数的形式调用 first,结果为 1
jq -nr 'first([1,2,3]|.[])'
#下面的表达式以 filter 形式调用 first
jq -nr '[1,2,3]|.[]|first'

#nth 函数的使用,结果为 2
jq -nr 'nth(1;[1,2,3]|.[])'

自定义功能和模块化

jq作为类似于编程语言的表达系统,也提供了定义函数的能力。 其语法规则为: def () : ; 定义函数时,需要注意以下规则。

在许多情况下,函数参数被作为表达式引用,类似于其他编程语言中的函数。

地图函数源码

1
2
3

def map(f): [.[] | f];
#下面表达式的结果是 20,因为当作参数传入的表达式在函数 foo 中被引用两次
5|def foo(f): f|f;foo(.*2)

如果希望传入的参数作为简单值使用,则需要将参数的值定义为同名变量,并以与使用变量相同的方式引用它。

值参数

1
2
3
4
5
6
7

#下面表达式结果为 10,传入的表达式'.*2'在函数 foo 中首先被求值。
5|def foo(f): f as $f|$f|$f;foo(.*2)
#上面的表达式可以简写为如下形式,注意,引用参数时必须带$。
5|def foo($f): $f|$f;foo(.*2)
#否则等于直接引用参数中的表达式。
#例如下面的表达式结果为 20
5|def foo($f): $f|f;foo(.*2)

子函数可以在函数内部定义。 利用这个特性我们可以实现递归函数。

递归函数实现数组求和

1
2

#下面表达式的结果是 15
jq -nr '[1,2,3,4,5]|def total: def _t: .|first+(if length>1 then .[1:]|_t else 0 end); _t;total'

除了在表达式内部定义函数外,我们还可以在外部文件中编写自定义函数,形成单独的类库。 jq有完整的模块系统来支持自定义类库。

首先,可以使用命令行参数'-L'来指定jq搜索模块时需要搜索的路径。

其次,模块内部可以通过指令与指令实现相互引用。 在 quote 指令中,有几个特殊的路径前缀需要解释。

当指令引用模块 foo/bar 时,jq 将在搜索路径中查找 foo/bar.jq 或 foo/bar/bar.jq。

转自:极其强大的shell json解析工具jq

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON