JQ

JQ

简介

jq是一个shell下的json解析工具,功能强大。

用法

格式化json

不加任何选项和表达式的作用是格式化json字符串,比较实用

1
2
3
$ echo '{"key":"va", "key2":"val2"}'|jq  #格式化echo里面的json
$ cat t.txt|jq                           #格式化t.txt里面的json
$ jq . t.txt                             #jq打开文件,并格式化t.txt里面的json

根据key查询json的值

根据key查询json可以写成.key,如果key是特殊的字符,比如全数字需要用引号括起来: ."key"
.foo.bar的形式类似于shell的管道符|,.foo.bar等于.foo|.bar

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#如果key对应的val存在则输出val
$ echo '{"foo": 42, "bar": "less interesting data"}' | jq '.foo'
> 42    

#如果key对应的val值不存在则输出null
$ echo '{"notfoo": true, "alsonotfoo": false}' | jq '.foo'
> null  

$ echo '{"111":"222", "333":"444"}'| jq '.["111"]'
> "222" 

#效果和上面一条一样
$ echo '{"111":"222", "333":"444"}' | jq '."111"' 
> "222" 

## 根据key, 更新value
$ echo '{"111":"222", "333":"444"}' | jq '."111" = "aaa" '
> '{"111":"aaa", "333":"444"}' 

## 根据key, 更新value, 使用变量
$ echo '{"111":"222", "333":"444"}' | jq -- '."111" = "aaa" '
> '{"111":"aaa", "333":"444"}' 

数组

 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
#查询数组第1个元素
$ echo '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' | jq '.[0]'
#输出
{ "name": "JSON",  "good": true }

#查询第3个元素,需要注意的是数组的下标是从0开始算的
$ echo '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' |jq '.[2]'
#输出
null

#查询下标为2到3之间的元素
$ echo '["a","b","c","d","e"]'|jq '.[2:4]'
#输出
[  "c",  "d" ]

#查询下标为3之前的元素
$ echo '["a","b","c","d","e"]'|jq '.[:3]'
#输出
[  "a",  "b",  "c" ]

#查询倒数第1-2个元素
$ echo '["a","b","c","d","e"]'|jq '.[-2:]'
> [ "d",  "e" ]

#查询倒数第2个元素
$ echo '["a","b","c","d","e"]' jq '.[-2]'
> "d"

.[]: 查询所有val

可以使用.[]语法,查询json对象的所有值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#拿到key的val以及key2的val2
$ echo '{"key":"val", "key2":"val2"}' | jq '.[]'
> "val"
> "val2"

#拿到key的val(一个object),key2的val
$ echo '{"key":{"key3":"val3", "key4":"val4"}, "key2":"val2"}' | jq '.[]'
#输出
> {  "key3": "val3",  "key4": "val4" }
> "val2"

#拿到json数组里面的值
$ echo ' [{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' | jq '.[]'
> { "name": "JSON",  "good": true } { "name": "XML",  "good": false }

,:多条件

如果要写多个过滤条件使用,号,现输出,左边的结果在输出,右边的结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ echo '{"foo": 42, "bar": "something else", "baz": true}' | jq '.foo,.bar,.baz'
#输出
42
"something else"
true

#.key和[]表达式可以组合使用
$ echo '{"user":"stedolan", "projects": ["jq", "wikiflow"]}'|jq '.user,.projects[]'
#输出
"stedolan"
"jq"
"wikiflow"

#可以使用,一次查询数组里的多个元素
$ echo '["a","b","c","d","e"]'|jq '.[4,3]'
#输出
"e"
"d"

|:管道符号

shell里面的|是连接各个shell命令的通道,像大管道套小管道,过滤器就是命令,可以很方便的过滤出想要的数据来
jq里面也有|符号

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#可以先用.[]拿到值,再使用|(管道)拿到name
$ echo '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]'|jq '.[] | .name'
#输出
"JSON"
"XML"

######把查询结果包装成一个数组(array)--使用[]符号
[]在jq里面表示数组,可以现查询再使用[]把查询结果包装成数组
$ echo '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]'|jq '[.[]|.name]'
#输出
[ "JSON",  "XML" ]

{}符号

把查询结果包装成一个对象(object)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#修改json的key名
$ echo '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}'| jq '{user1: .user, title2: .titles}'
> {  "user1": "stedolan",  "title2": [    "JQ Primer",    "More JQ" ]}

#如果其中一个表达式产生多个结果,那最终生成的json也有多个结果
#其中.titles[]会查询出两个结果,那最终生成的json也是两个
$ echo '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}'|jq '{user, titles:.titles[]}'
> {  "user": "stedolan",  "titles": "JQ Primer" }{  "user": "stedolan",  "titles": "More JQ"}

#如果想使用原来的json某个key的值作新的json的key,可以使用(.key)语法
$ echo '{"user":"stedolan","titles":["JQ Primer", "More JQ"]}'| jq '{(.user): .titles}'
> {  "stedolan": ["JQ Primer", "More JQ" ]}

+运算符

+运算符需要两个相同输入,并把结果加在一起

  • 数字常规的加法
  • array拼接成一个大的数组
  • string拼接成一个大的string
  • object也是合并操作,如果有两个key相同的object新的覆盖旧的

null可以与任何值相加,返回另外一个值

 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
#数字相加
$ echo '{"a":1}'| jq '.a + 1'
#输出
2

#array相加
$ echo '{"a": [1,2], "b": [3,4]}' | jq '.a+.b'
#输出
[  1,  2,  3,  4]

#string相加
$ echo '{"a": "hello", "b": "world"}'|jq '.a+.b'
#输出
"helloworld"

#object相加
$ echo null | jq '{a: 42} + {b: 2} + {c: 3} + {a: 1}'
#输出
{  "a": 1,  "b": 2,  "c": 3}

#有空值相加的情况
$ echo '{"a": 1}'|jq 'null +.a'
#输出
1

$ echo '{}'|jq '.a+1'
#输出
1

-运算符

-号运算符用于数字,用于数组,会在第一个数组删除第二个数组中出现的所有项

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#数字相减
$ echo '{"a":4}'|jq '4 - .a'
#输出
0

#数组相减
$ echo '  ["xml", "yaml", "json"]' |jq '. - ["xml", "json"]'
#输出
[  "yaml"
]

乘法*除法运算符

  • /只能用在数字类型上

    1
    2
    3
    
    $ echo 5|jq '10 / . * 3'
    #输出
    6
    

length

length用于不同类型值的长度

  • string:返回字符串中字符的个数,如果有中文返回中文的个数
  • array: 返回数组元素的个数
  • object: 返回键-值对的个数
1
2
3
echo '["郭", [1,2], "string", {"a":2}, null]'| jq '.[]|length'
#输出
12610

keys and keys_unsorted

keys可以返回json键名数组,其中keys与keys_unsorted区别是keys返回的数组是排过序的
keys_unsorted返回的数组是不排序
当json的顶层元素是数组时,keys返回数组的下标

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ echo '{"abc": 1, "abcd": 2, "Foo": 3}'|jq 'keys'
#输出
[  "Foo",  "abc",  "abcd" ]

$ echo '{"abc": 1, "abcd": 2, "Foo": 3}'|jq 'keys_unsorted'
#输出
[  "abc",  "abcd",  "Foo" ]

$ echo '["aaa", "bbb", "ccc"]'|jq 'keys'
#输出
[  0,  1,  2]

has(key)

返回输入的json对象是否有给定的key,或者数组存在指定的索引,有返回true,没有返回false

1
2
3
4
5
6
7
$ echo '[{"foo":123, "abc":456}, {"cde":789, "fgh":111}]' | jq 'map(has("foo"))'
#输出
[  true,  false ]

$ echo '[[0,1], ["a","b","c"]]'|jq 'map(has(2))'
#输出
[  false,  true ]

in

检查输入的键是否在对象中,或者输入的索引存在数组中,有返回true,没有返回false,本质上是反过来的has

1
2
3
4
5
6
7
8
9
$ echo '["foo", "123"]'| jq '.[]|in({"foo":123})'
#输出
true
false

$ echo '[2, 3]'| jq '.[]|in(["a", "b", "c"])'
#输出
true
false

path

1
#TODO

del

del用来删除json 对象的键和值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#删除一个键值对
echo '{"aaa": 111, "bbb": 222, "ccc":33}'|jq 'del(.aaa)'
#输出
{  "bbb": 222,  "ccc": 33}

#删除多个键值对
echo '{"aaa": 111, "bbb": 222, "ccc":33}'|jq 'del(.["aaa", "bbb"])'
#输出
{  "ccc": 33}

#根据下标删除数组元素
echo '[111, 222, 33]'|jq 'del(.[1,2])'
#输出
[  111]

to_entries, from_entries, with_entries

  • to_entries 把json对象转成键-值对数值
  • from_entries 键-值对数组转成json对象
  • with_entries 是 to_entries | map(foo) | from_entries 的缩写
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ echo '{"aa":11, "bb":22}'|jq 'to_entries'
#输出
[  {    "key": "aa",    "value": 11  },  {    "key": "bb",    "value": 22  }]

$ echo '[{"key" : "aa", "value": "11"}, {"key":"bb", "value": "22"}]'|jq 'from_entries'
#输出
{  "aa": "11",  "bb": "22"
}

#修改key名
$ echo '{"a": 1, "b": 2}'|jq 'with_entries(.key |= "student_" + .)'
#输出
{  "student_a": 1,  "student_b": 2}

select

select 是过滤器,里面可以写表达式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#选择数组里面的偶数
$ echo [1,2,3,4,5,6]|jq 'map(select(.%2 == 0))'
#输出
[
  2,
  4,
  6
]

$ echo  '[{"id": "first", "val": 1}, {"id": "second", "val": 2}]'| jq '.[]|select(.id=="first")'
#输出
{
  "id": "first",
  "val": 1
}

实例

 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
{
  "ID": 0,
  "Name": "ltptest",
  "Owner": "ltptest",
  "DpReplicaNum": 3,
  "MpReplicaNum": 3,
  "Status": 0,
  "Capacity": 30,
  "RwDpCnt": 25,
  "MpCnt": 1,
  "DpCnt": 25,
  "AvailSpaceAllocated": 3000,
  "Tokens": {
    "bHRwdGVzdCMxIzE1NzY4MjU1MzY=": {
      "TokenType": 1,
      "Value": "bHRwdGVzdCMxIzE1NzY4MjU1MzY=",
      "VolName": "ltptest"
    },
    "bHRwdGVzdCMyIzE1NzY4MjU1MzY=": {
      "TokenType": 2,
      "Value": "bHRwdGVzdCMyIzE1NzY4MjU1MzY=",
      "VolName": "ltptest"
    }
  }
}
1
2
$ jq '.Tokens | .[] | select( .TokenType == 2) | .Value'
"bHRwdGVzdCMyIzE1NzY4MjU1MzY="
 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
41
42
43
44
45
46
47
48
49
50
51
# 获取 docker image下载量
$ curl "https://hub.docker.com/v2/repositories/chubaofs/" 2>/dev/null | \>
    jq  'reduce (.results | .[] | .pull_count ) as $item (0; .+$item) '

# strftime
$ echo '[ { "CreateTime": 1629968504 }, { "CreateTime": 130675857 } ]' | \
    jq '.[].CreateTime |= strftime("%Y-%m-%d_%H:%M:%S")'

# 计算百分比及格式化dataSize
echo '[ { "UseSize": 1629968504, "TotalSize": 10222023 }, { "UsedSize": 130675857, "TotalSize": 2132 } ]' | \
jq '[ 
        .[] | . + { Ratio: (.UsedSize/.TotalSize | .*100 | floor | ./100 ) } | 
        ( 
            ["B", "KB", "MB", "GB", "TB", "PB", "ZB"] as $unit | 
            .TotalSize |=  ( . as $size | 
                ($size | [until(.<1024; ./1024)] | .[0] | .*100 | floor | ./100 | tostring) + 
                "_" + 
                ($size | log10 | ./3 | floor as $l | $unit[$l] )    
             )
        ) 
    ] '

>
[
  {
    "UsedSize": 1629968504,
    "TotalSize": "9.74MB",
    "Ratio": 0
  },
  {
    "UsedSize": 130675857,
    "TotalSize": "2.08KB",
    "Ratio": 0
  }
]


## 更新对象属性数组中的某一属性值
$ echo '{"Replicas":[ { "ReportTime": 1629968504 }, { "ReportTime": 130675857 }  ]}' | \
    jq -r '.Replicas[] |= (.ReportTime |= strftime("%Y-%m-%dT%H:%M:%S"))'
>
{
  "Replicas": [
    {
      "ReportTime": "2021-08-26T09:01:44"
    },
    {
      "ReportTime": "1974-02-21T10:50:57"
    }
  ]
}

参考

  1. https://gist.github.com/olih/f7437fb6962fb3ee9fe95bda8d2c8fa4
  2. jq
updatedupdated2024-08-252024-08-25