Back to prev

jq in action

May 11, 2022
Linkang Chan
@Jesse Chan

日常编写 bash 脚本处理 json 时,我们大概率会使用 jq 这个工具,此时我们需要学习一些 jq 的语法,下面总结一些使用过程中遇到的一些问题和解决方式。

使用 bash 变量

$ var1="hello"
$ jq --arg var1 $var1 '.res[$var1]' filename.json

可以多次使用--arg inner_var bash_var的方式。

检查 key 是否存在

$ jq -r '.result | has("keyname")' filename.json
true  # or false

检查字段是否存在某些字段

$ jq -r '.result.var | contains("content")' filename.json
true # or false

获取 object 的 key 或者 value

比如有如下的 json :

{
  "key1": {
    "key11": {
      "key111": {
        "key1111": "hello"
      }
    }
  },
  "key2": {
    "key22": {
      "key222": {
        "key2222": "world"
      }
    }
  }
}

若只想输入两个 value 的值,helloworld。 可以用的方式是:

$ jq -r '.[] | .[] | .[] | to_entries | .[].value' xxx.json
hello
world

注意这边的to_entries是将内容转变成固定格式:

[
  {
    "key": "key111",
    "value": {
      "key1111": "hello"
    }
  }
]
[
  {
    "key": "key222",
    "value": {
      "key2222": "world"
    }
  }
]

之后再统一取指定的 key 下面的内容。

jq 读取 array 内容到 bash 变量

我们经常需要将 json 的数组内容读取到 bash 的数组中。可以有两种方式,主要看 bash 的版本:

  • bash 4+
$ mapfile -t arr < <(jq -r 'keys[]' xxx.json)
  • older bash
arr=()
while IFS='' read -r line; do
   arr+=("$line")
done < <(jq 'keys[]' xxx.json)

此外还有一种方式用于纯 json 数组的转换,比如:

#[
#    1, 2, 3, 4, 5, 6, 7, 8, 9
#]

$ arr=($(jq -r '. | @sh' xxx.json))

@sh: The input is escaped suitable for use in a command-line for a POSIX shell. If the input is an array, the output will be a series of space-separated strings.

jq 输出多字段内容读取到 bash 变量中

当我们同时需要读取两个并列的 json 字段的内容到脚本中同时处理时,这个过程稍微有点复杂。

 jq -r '. | [.name,.value] | @tsv' model_info.json | \
        while IFS=$'\t' read -r name ctx; do
           echo $name, $ctx
        done

@tsv: The input must be an array, and it is rendered as TSV (tab-separated values). Each input array will be printed as a single line. Fields are separated by a single tab (ascii 0x09). Input characters line-feed (ascii 0x0a), carriage-return (ascii 0x0d), tab (ascii 0x09) and backslash (ascii 0x5c) will be output as escape sequences \n, \r, \t, \ respectively.

总体的思路就是先转成 tsv 格式,后续在 read 指定分隔符读取。

join 指定字段内容

join 是使用指定分隔符去合并相应的数组内容,比如有数组:[1,2,3,4,5]

$ jq -r '.|join("|")' arr.json
1|2|3|4|5