Linux中的JQ命令简介
1. 概述
JSON 是一种广泛使用的结构化数据格式,通常用于大多数现代 API 和数据服务。由于其轻量级特性和与 JavaScript 的兼容性,它在 Web 应用程序中特别受欢迎。
**不幸的是,像 Bash 这样的 shell 不能直接解释和使用 JSON。*这意味着通过命令行使用 JSON 可能很麻烦,涉及使用sed 和grep *等工具的组合进行文本操作。
在本教程中,我们将看看如何使用jq 来缓解这种尴尬——一个雄辩的 JSON 命令行处理器。
2. 安装
让我们从安装 jq开始,它在大多数操作系统打包存储库中都可用。也可以直接下载 二进制文件或从源代码构建它。
一旦我们安装了包,让我们通过运行jq来验证安装:
$ jq
jq - commandline JSON processor [version 1.6]
Usage: jq [options] <jq filter> [file...]
jq [options] --args <jq filter> [strings...]
jq [options] --jsonargs <jq filter> [JSON_TEXTS...]
...
如果安装成功,我们将在控制台中看到版本、一些使用示例和其他信息。
3. 使用简单的过滤器
** jq是围绕在 JSON 流上工作的过滤器的概念构建的。**每个过滤器接受一个输入并将 JSON 发送到标准输出。正如我们将要看到的,我们可以使用许多预定义的过滤器。我们可以使用管道轻松组合这些过滤器,以快速构建复杂的操作和转换并将其应用于我们的 JSON 数据。
3.1. 美化 JSON
让我们从最简单的过滤器开始,顺便说一下,它是jq最有用和最常用的功能之一:
echo '{"fruit":{"name":"apple","color":"green","price":1.20}}' | jq '.'
我们*echo 一个简单的 JSON 字符串并将其直接通过管道传输到我们的jq命令中。然后我们使用身份过滤器“。” 它接受输入并将其作为输出原样生成,但需要注意的是默认情况下jq*会漂亮地打印所有输出。
这给了我们以下输出:
{
"fruit": {
"name": "apple",
"color": "green",
"price": 1.2
}
}
我们还可以将此过滤器直接应用于 JSON 文件:
jq '.' fruit.json
当我们想要从 API 检索数据并以清晰易读的格式查看响应时,能够美化 JSON 特别有用。
让我们使用curl 打一个简单的 API 来在实践中看到这一点:
curl http://api.open-notify.org/iss-now.json | jq '.'
这为我们提供了国际空间站当前位置的 JSON 响应:
{
"message": "success",
"timestamp": 1572386230,
"iss_position": {
"longitude": "-35.4232",
"latitude": "-51.3109"
}
}
3.2. 访问属性
**我们可以使用另一个简单的过滤器访问属性值:.field 运算符。**要查找属性值,我们只需将此过滤器与属性名称组合在一起。
让我们通过我们简单的水果示例来了解这一点:
jq '.fruit' fruit.json
在这里,我们正在访问 fruit 属性,它为我们提供了这个键的所有孩子:
{
"name": "apple",
"color": "green",
"price": 1.2
}
我们还可以将属性值链接在一起,允许我们访问嵌套对象:
jq '.fruit.color' fruit.json
正如预期的那样,这只是返回我们水果的颜色:
"green"
如果我们需要检索多个键,我们可以使用逗号分隔它们:
jq '.fruit.color,.fruit.price' fruit.json
这将产生一个包含两个属性值的输出:
"green"
1.2
请注意,如果其中一个属性包含空格或特殊字符,我们需要在从jq命令访问它时将属性名称用引号引起来:
echo '{ "with space": "hello" }' | jq '."with space"'
4. JSON 数组
**现在让我们看看如何使用 JSON 数据中的数组。**我们通常使用数组来表示项目列表。和许多编程语言一样,我们使用方括号来表示数组的开始和结束。
4.1. 迭代
我们将从一个基本示例开始,以演示如何迭代数组:
echo '["x","y","z"]' | jq '.[]'
在这里,我们看到对象值迭代器运算符*.[]*正在使用中,它将在单独的行上打印出数组中的每个项目:
"x"
"y"
"z"
现在让我们假设我们想要在 JSON 文档中表示水果列表:
[
{
"name": "apple",
"color": "green",
"price": 1.2
},
{
"name": "banana",
"color": "yellow",
"price": 0.5
},
{
"name": "kiwi",
"color": "green",
"price": 1.25
}
]
数组中的每一项都是代表水果的对象。 让我们看看如何从数组中的每个对象中提取每个水果的名称:
jq '.[] | .name' fruits.json
首先,我们使用 .[] 遍历数组。然后我们可以使用管道 | 将数组中的每个对象传递给命令中的下一个过滤器。最后一步是使用*.name*从每个对象中输出名称字段:
"apple"
"banana"
"kiwi"
我们还可以使用更简洁的版本,直接访问数组中每个对象的属性:
jq '.[].name' fruits.json
4.2. 按索引访问
当然,与所有数组一样,我们可以通过传递索引直接访问数组中的一项:
jq '.[1].price' fruits.json
4.3. 切片
**最后,jq还支持对数组进行切片,这是另一个强大的功能。**这在我们需要返回数组的子数组时特别有用。 同样,让我们使用一个简单的数字数组来看看这个:
echo '[1,2,3,4,5,6,7,8,9,10]' | jq '.[6:9]'
结果将是一个长度为 3 的新数组,包含从索引 6(包括)到索引 9(不包括)的元素:
[
7,
8,
9
]
使用切片功能时,也可以省略其中一个索引:
echo '[1,2,3,4,5,6,7,8,9,10]' | jq '.[:6]' | jq '.[-2:]'
由于我们仅在*.[:6]*中指定了第二个参数,因此切片将从数组的开头开始并一直运行到索引 6。这与执行 *.[0:6]*相同。
第二个切片操作有一个负参数,在这种情况下表示它从数组的末尾向后计数。
注意第二个切片的细微差别——我们将索引作为第一个参数传递。这意味着我们将从末尾(-2)开始两个索引,并且由于第二个参数为空,它将一直运行到数组的末尾。
这给了我们以下输出:
[
5,
6
]
5. 使用函数
jq有许多强大的内置函数,我们可以使用它们来执行各种有用的操作。现在让我们来看看其中的一些。
5.1.获取密钥
有时,我们可能希望以数组而不是值的形式获取对象的键。 我们可以使用keys函数来做到这一点:
jq '.fruit | keys' fruit.json
这给了我们按字母顺序排序的键:
[
"color",
"name",
"price"
]
5.2. 返回长度
数组和对象的另一个方便的函数是length函数。
我们可以使用此函数返回数组的长度或对象的属性数:
jq '.fruit | length' fruit.json
在这里,我们得到“3”,因为水果对象具有三个属性。 我们甚至可以对字符串值使用length函数:
jq '.fruit.name | length' fruit.json
我们会看到“5”作为结果输出,因为水果名称属性有五个字符:“apple”。
5.3. 映射值
** map函数是一个强大的函数,我们可以使用它来将过滤器或函数应用于数组**:
jq 'map(has("name"))' fruits.json
**在此示例中,我们将has函数应用于数组中的每个项目,并查看是否存在 name 属性。**在我们的简单水果 JSON 中,我们在每个结果项中都为真。
我们还可以使用map函数将操作应用于数组中的元素。
假设我们想提高每种水果的价格:
jq 'map(.price+2)' fruits.json
这为我们提供了一个新数组,每个价格都增加:
[
3.2,
2.5,
3.25
]
5.4. 最小值和最大值
如果我们需要找到输入数组的最小或最大元素,我们可以使用min和max函数:
jq '[.[].price] | min' fruits.json
同样,我们也可以在 JSON 文档中找到最昂贵的水果:
jq '[.[].price] | max' fruits.json
请注意,在这两个示例中,我们 在数组迭代周围使用*[]构造了一个新数组。**这仅包含我们将这个新列表传递给min或max*函数之前的价格。**
5.5. 选择值
** select函数是另一个令人印象深刻的实用程序,我们可以使用它来查询 JSON。**
我们可以认为它有点像 JSON 的 XPath 的简单版本:
jq '.[] | select(.price>0.5)' fruits.json
这将选择价格大于 0.5 的所有水果。
同样,我们也可以根据属性的值进行选择:
jq '.[] | select(.color=="yellow")' fruits.json
我们甚至可以结合条件来构建复杂的选择:
jq '.[] | select(.color=="yellow" and .price>=0.5)' fruits.json
这将为我们提供与给定价格条件匹配的所有黄色水果:
{
"name": "banana",
"color": "yellow",
"price": 0.5
}
5.6. 支持正则表达式
**接下来,我们将查看test函数,它使我们能够测试输入是否与给定的正则表达式 **匹配:
jq '.[] | select(.name|test("^a.")) | .price' fruits.json
简单地说,我们要输出名称以字母“a”开头的所有水果的价格。
5.7. 寻找独特的价值
一个常见的用例是能够查看数组中特定值的唯一出现或删除重复项。
让我们看看水果 JSON 文档中有多少种独特的颜色:
jq 'map(.color) | unique' fruits.json
我们使用map函数创建一个只包含颜色的新数组。然后我们使用管道 |将新数组中的每种颜色传递给unique函数。
这给了我们一个具有两种不同水果颜色的数组:
[
"green",
"yellow"
]
5.8. 从 JSON 中删除密钥
我们有时还想从 JSON 对象中删除一个键和相应的值。
为此,jq提供了del函数:
jq 'del(.fruit.name)' fruit.json
这将输出没有删除键的水果对象:
{
"fruit": {
"color": "green",
"price": 1.2
}
}
6. 转换 JSON
通常在使用 JSON 等数据结构时,我们可能希望将一种数据结构转换为另一种数据结构。当我们只对几个属性或值感兴趣时,这在处理大型 JSON 结构时很有用。
在此示例中,我们将使用一些描述页面条目列表的 Wikipedia JSON:
{
"query": {
"pages": [
{
"21721040": {
"pageid": 21721040,
"ns": 0,
"title": "Stack Overflow",
"extract": "Some interesting text about Stack Overflow"
}
},
{
"21721041": {
"pageid": 21721041,
"ns": 0,
"title": "Blogdemo",
"extract": "A great place to learn about Java"
}
}
]
}
}
我们只对每个页面条目的标题和摘录真正感兴趣。
那么,让我们看看如何转换这个文档:
jq '.query.pages | [.[] | map(.) | .[] | {page_title: .title, page_description: .extract}]' wikipedia.json
我们将更详细地查看该命令以正确理解它:
- 首先,我们首先访问 pages 数组,然后通过管道将该数组传递给命令中的下一个过滤器。
- 然后我们遍历这个数组并将 pages 数组中的每个对象传递给map函数,在这里我们只需创建一个包含每个对象内容的新数组。
- 接下来,我们遍历这个数组并为每个项目创建一个包含两个键page_title和page_description的对象。
- .title和.extract引用用于填充两个新键。
这为我们提供了一个新的、精简的 JSON 结构:
[
{
"page_title": "Stack Overflow",
"page_description": "Some interesting text about Stack Overflow"
},
{
"page_title": "Blogdemo",
"page_description": "A great place to learn about Java"
}
]