Contents

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. 最小值和最大值

如果我们需要找到输入数组的最小或最大元素,我们可以使用minmax函数

jq '[.[].price] | min' fruits.json

同样,我们也可以在 JSON 文档中找到最昂贵的水果:

jq '[.[].price] | max' fruits.json

请注意,在这两个示例中,我们 在数组迭代周围使用*[]构造了一个新数组。**这仅包含我们将这个新列表传递给minmax*函数之前的价格。**

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_titlepage_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"
  }
]