# shell中处理json数据工具jq

在维护系统中，经常会遇到需要处理一些程序的json格式配置文件，并且在curl脚本处理RESTful返回数据。

如果输出内容没有很好格式化，则大量的json字符串非常不利于检查。此时，使用python模块 `json.tool` 过滤就可以输出标准格式json内容：

```
container_id=f4810fec374a096895c90e1c74574a7d69e5ec306ea8fe0817e461c28b3da716
cat /var/run/docker/libcontainerd/${container_id}/config.json | python -m json.tool
```

输出内容是非常清晰的json格式:

```
{
    "annotations": {
        "__BlkBufferWriteBps": "0",
        "__BlkBufferWriteSwitch": "0",
        ...
```

有一个处理脚本案例结合了python json.tool和sed等:

```bash
curl -s 'http://api.icndb.com/jokes/random' \
| python -m json.tool \
| grep '\"joke\"' \
| cut -d ':' -f 2 \
| sed 's/&quot;/\"/g'
```

虽然我们可以用shell脚本来处理输出的json数据，但是，如果要依赖组合的 `awk` `grep` `cut` 显然非常繁琐，效率低下。

实际上工具 [jq](https://stedolan.github.io/jq/) 可以帮助我们在shell脚本中处理json数据，特别适合解析一些REST返回数据。

举例

```
curl -s "http://api.icndb.com/jokes/random" | jq '.value.joke'
```

举例，json数据如下

```javascript
{ "foo": 123, "bar": 456 }
```

我们要提取 foo 键的数据，则使用命令

```bash
echo '{ "foo": 123, "bar": 456 }' | jq '.foo'
```

输出结果就是 `123`

```bash
echo '{ "Version Number": "1.2.3" }' | jq '."Version Number"'
```

## jq处理array

* jq 可以处理数组，使用 `.[]` 方法:

```bash
echo '[1,2,3]' | jq '.[]'
```

对于数组的对象，采用如下方法:

```bash
echo '[ {"id": 1}, {"id": 2}  ]' | jq '.[].id'
```

也可以用来获取 `key/value` 对的值:

```bash
echo '{ "a": 1, "b": 2  }' | jq '.[]'
```

可以按照索引来获得array的值：

```bash
echo '["foo", "bar"]' | jq '.[1]'
```

jq还包含了一些内建功能：

* 返回array的key:

```
echo '{ "a": 1, "b": 2  }' | jq 'keys | .[]'
```

则返回值是a和b。请注意，在jq中也可以使用管道`|`，这里就是表示提取出keys以后在通过索引`.[]`把所有key都输出出来。

* 可以返回array的长度

```bash
echo '[1,2,3]' | jq 'length'
```

## 使用jq创建对象

可以对json进行提取重整，例如:

```bash
echo '{"user": {"id": 1, "name": "Cameron"}}' | jq '{ name: .user.name  }'
```

则输出就是

```javascript
{
  "name": "Cameron"
}
```

## 去除引号

默认jq输出的结果有双引号，这时候我们存储到字符串变量中会多出这对引号，虽然可以通过 `tr` 命令剥离，但是太麻烦了。实际上， `jq` 就有这个内置功能，参数 `-r` 会移除引号:

```bash
jq -r '.name' <json.txt
```

## 多字段提取方法

```javascript
{
    "users": [
        {
            "first": "Stevie",
            "last": "Wonder"
        },
        {
            "first": "Michael",
            "last": "Jackson"
        }
    ]
}
```

如果需要一次提出多个字段，可以使用以下方法

```bash
jq '.users[] | .first + " " + .last'
```

输出是：

```
Stevie Wonder
Michael Jackson
```

这个方法非常优秀，我用它来处理一些非常复杂的嵌套json效率极高

类似也可以采用，输出效果相同

```bash
jq '.users[] | "\(.first) \(.last)"'
```

另外一种多字段输出会换行：

```bash
jq '.users[] | ".first , .last'
```

## json字符串错误`INvalid string`

我在使用脚本生成的json文件，发现 `jq` 解析报错

```bash
parse error: Invalid string: control characters from U+0000 through U+001F must be escaped at line 264, column 1
```

在vs code中观察，可以看到报错提示: `Unexpected end of string.json(258)`

原因是我想在 "text" 中传递大段的markdown文本，但是很不幸，json规范不支持真正的行回车，只能通过 `\n` 来打断行。 - 参考 [Unexpected end of string.json(258)](https://stackoverflow.com/questions/2392766/are-multi-line-strings-allowed-in-json) 可以看到:

> structure your data: break the multiline string into an array of strings, and then join them later on.

所以要将

```javascript
{
    "text" : "line 1
    line 2
    line 3
    "
}
```

修订为:

```javascript
{
    "text" : "line 1\nline 2\nline 3"
}
```

## 参考

* [Working with JSON in bash using `jq`](https://medium.com/cameron-nokes/working-with-json-in-bash-using-jq-13d76d307c4)
* [Working with JSON in bash using jq](https://cameronnokes.com/blog/working-with-json-in-bash-using-jq/)
* [How to remove double-quotes in jq output for parsing json files in bash? ](https://stackoverflow.com/questions/44656515/how-to-remove-double-quotes-in-jq-output-for-parsing-json-files-in-bash)
* [Using jq to parse and display multiple fields in a json serially](https://stackoverflow.com/questions/28164849/using-jq-to-parse-and-display-multiple-fields-in-a-json-serially)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://huataihuang.gitbook.io/cloud-atlas-draft/develop/shell/bash/json_jq.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
