如何使用 jq 从 Linux 命令行解析 json 文件
JSON(JavaScript 对象表示法)格式广泛用于表示数据结构,并且经常用于在应用程序的不同层之间交换数据,或者通过使用 API 调用来交换数据。我们可能知道如何使用最常用的编程语言与 json 格式的数据进行交互,例如使用 python 解析 JSON,但是如果我们需要从命令行或 bash 脚本中与其交互怎么办?在本文中,我们将了解如何使用 jq
实用程序完成此类任务,并了解其基本用法。
在本教程中您将学习:
如何在最常用的 Linux 发行版中安装 jq 或从源代码编译它
如何使用jq解析json格式数据
如何使用“,”和“|”组合过滤器
如何使用 length、keys、has 和 map 函数
安装
jq
实用程序包含在所有主要 Linux 发行版存储库中,因此安装它非常简单:我们只需要使用我们最喜欢的包管理器。如果我们使用 Debian 或基于 Debian 的发行版(例如 Ubuntu 或 Linux Mint),我们可以使用 apt:
$ sudo apt install jq
如果我们更喜欢 Red Hat 系列发行版,例如 Fedora、CentOS 或 RHEL,我们可以通过 dnf
包管理器安装 jq
(在这些发行版的最新版本中)它取代了 yum 的发行版)。要安装该软件包,我们将运行:
$ sudo dnf install jq
在 Archlinux 上安装 jq
也同样简单。分发包管理器是 pacman,该包可在社区存储库中找到。我们可以使用以下命令来执行安装:
$ sudo pacman -S install jq
如果我们不能,或者由于某种原因我们不想使用预构建的二进制包,我们可以从源代码编译 jq。在 下面几行我们描述了所需的步骤。
从源代码构建和安装
要从源代码构建和安装 jq,我们必须做的第一件事是下载发布 tarball。此刻 编写时,最新的可用版本是 1.6
。要在不离开终端的情况下下载 tarball,我们可以使用 wget
:
$ wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-1.6.tar.gz
下载完成后,我们必须解压缩并提取 tarball:
$ tar -xzf jq-1.6.tar.gz
下一步是进入由上一个命令创建的 jq-1.6
目录:
$ cd jq-1.6
现在,要编译源代码,我们需要以下实用程序:
海湾合作委员会
汽车制造商
库工具
制作
要构建我们运行的软件:
$ autoreconf -fi
$ ./configure && make && sudo make install
默认情况下,make install
命令会将二进制文件安装到 /usr/local/bin
目录中,并将库安装到 /usr/local/lib 中
。如果我们想要自定义安装并更改这些目录,则必须在启动 ./configure
脚本时使用 --prefix
选项指定不同的前缀。
例如,要仅为特定用户安装软件,我们可以传递 $HOME/.local
目录作为前缀:在这种情况下,二进制文件将安装到 $HOME/.local/ 中bin
和库放入 $HOME/.local/lib
;通过这样的配置,无需使用管理权限启动 make install
命令。如果您想了解如何更好地组织从源代码安装的软件,您可以查看我们关于 GNU stow 实用程序的文章。
用法
一旦我们安装了jq
,我们就可以使用它从命令行解析json文件。为了本教程的目的,我们将使用一个简单的数据结构,其中包含有关《指环王》书中三个角色的一些详细信息。数据保存到 characters.json
文件中。
jq
实用程序的工作原理是对 json 数据流应用过滤器。首先,我们将使用最简单的过滤器 .
,它返回未更改但打印精美的输入数据。针对这个特性,可以利用它以更易读的方式格式化数据:
$ jq . characters.json
上面的命令产生以下输出:
{
"characters": [
{
"name": "Aragorn",
"race": "man"
},
{
"name": "Gimli",
"race": "dwarf"
},
{
"name": "Legolas",
"race": "elf"
}
]
}
现在,假设我们想要过滤数据以仅获取与 characters
键关联的值。为了完成该任务,我们提供键的名称,并获取其值(如果不存在则获取 null
):
$ jq .characters characters.json
在我们的示例中,与“characters”键关联的值是一个数组,因此我们获得以下结果:
[
{
"name": "Aragorn",
"race": "man"
},
{
"name": "Gimli",
"race": "dwarf"
},
{
"name": "Legolas",
"race": "elf"
}
]
如果我们只想获取数组的第一个元素怎么办?我们只需要从中“提取”正确的索引即可。知道数组是从零开始的,我们可以运行:
$ jq .characters[0] characters.json
该命令给了我们:
{
"name": "Aragorn",
"race": "man"
}
我们还可以获得数组的切片。例如,我们只想获取它的前两个元素。我们跑:
$ jq .characters[0:2] characters.json
该命令为我们提供了以下结果:
[
{
"name": "Aragorn",
"race": "man"
},
{
"name": "Gimli",
"race": "dwarf"
}
]
切片也适用于字符串,所以如果我们运行:
$ jq .characters[0].name[0:2] characters.json
我们获得“Aragorn”字符串的一个切片(前两个字母):"Ar"
。
单独访问数组元素
在上面的示例中,我们打印了“characters”数组的内容,该数组由三个描述幻想角色的对象组成。如果我们想迭代该数组怎么办?我们必须使其中包含的元素单独返回,因此我们必须使用 []
而不提供任何索引:
$ jq .characters[] characters.json
该命令的输出是:
{
"name": "Aragorn",
"race": "man"
}
{
"name": "Gimli",
"race": "dwarf",
"weapon": "axe"
}
{
"name": "Legolas",
"race": "elf"
}
在本例中,我们获得了 3 个结果:数组中包含的对象。可以使用相同的技术来迭代对象的值,在本例中是“characters”数组中包含的第一个值:
$ jq .characters[0][] characters.json
在这里我们得到以下结果:
"Aragorn"
"man"
“,”和“|”运营商
“,”和“|”运算符都用于组合两个或多个过滤器,但它们的工作方式不同。当两个过滤器用逗号分隔时,它们都会分别应用于给定的数据,让我们获得两个不同的结果。让我们看一个例子:
$ jq '.characters[0], .characters[2]' characters.json
首先使用 .characters[0]
过滤包含在 strings.json 文件中的 json 格式数据,然后使用 .charaters[2]
过滤,以获取第一个和第二个“characters”数组的第三个元素。通过执行上面的命令,我们获得两个单独结果:
{
"name": "Aragorn",
"race": "man"
}
{
"name": "Legolas",
"race": "elf"
}
“|”运算符的工作方式有所不同,其方式类似于 unix 管道。运算符左侧的过滤器产生的输出作为输入传递到运算符右侧的过滤器。如果运算符左侧的过滤器产生多个结果,则运算符右侧的过滤器将应用于其中的每一个结果:
$ jq '.characters[] | .name' characters.json
在此示例中,我们有两个过滤器。在运算符的左侧,我们有 .characters[]
过滤器,正如我们之前所看到的,它让我们获得“characters”数组的元素作为单独的结果。在我们的例子中,每个结果都是一个具有 "name"
和 "race"
属性的对象。 |
运算符右侧的 .name
过滤器应用于每个对象,因此我们得到以下结果:
"Aragorn"
"Gimli"
"Legolas"
功能
jq 实用程序包含一些非常有用的函数,我们可以将其应用于 json 格式的数据。我们现在将看到其中的一些:length
、keys
、has
和 map
。
长度函数
我们要讨论的第一个是length
,顾名思义,它让我们检索对象、数组和字符串的长度。对象的长度是其键值对的数量;数组的长度由它们包含的元素数量表示;字符串的长度是其组成的字符数。让我们看看如何使用该功能。假设我们想知道“字符”数组的长度,我们运行:
$ jq '.characters | length' characters.json
正如预期的那样,我们获得 3
作为结果,因为它是数组中元素的数量。以同样的方式,要获取数组中第一个对象的长度,我们可以运行:
$ jq '.characters[0] | length' characters.json
这次我们获得 2
作为结果,因为它是对象中包含的值对的数量。正如我们已经说过的,应用于字符串的相同函数会返回其中包含的字符数,因此,例如,运行:
$ jq '.characters[0].name | length' characters.json
我们收到结果 7
,这是“Aragorn”字符串的长度。
按键功能
keys
函数可以应用于对象或数组。在第一种情况下,它返回一个包含以下内容的数组: 对象键:
$ jq '.characters[0] | keys' characters.json
[
"name",
"race"
]
当应用于数组时,它返回另一个包含第一个数组的索引的数组:
$ jq '.characters | keys' characters.json
[
0,
1,
2
]
keys
函数返回已排序的元素:如果我们希望按插入顺序返回元素,可以使用 keys_unsorted
函数。
检查对象是否有键
我们可能想要对对象执行的一项非常常见的操作是检查它是否包含特定的键。为了完成这个任务,我们可以使用has
函数。例如,要检查 json 格式数据的主对象是否包含“weapons”键,我们可以运行:
$ jq 'has("weapons")' characters.json
false
在这种情况下,正如预期的那样,该函数返回 false
,因为该对象仅包含“characters”键:
$ jq 'has("characters")' characters.json
true
当应用于数组时,如果数组在给定索引处有元素,则该函数返回 true,否则返回 false:
$ jq '.characters | has(3)' characters.json
false
“characters”数组只有 3 个元素;数组是零索引的,因此检查数组是否作为与索引 3
关联的元素会返回 false
。
地图功能
map 函数让我们对给定数组的每个元素应用过滤器。例如,假设我们要检查“characters”数组中包含的每个对象中是否存在“name”键。我们可以这样组合 map
和 has
函数:
$ jq '.characters | map(has("name"))' characters.json
[
true,
true,
true
]
结论
在本文中,我们仅仅触及了 jq 实用程序提供的功能的表面,该实用程序允许我们从命令行解析和操作 json 格式的数据。我们学习了该程序的基本用法,“,”和“|”如何使用运算符的工作原理,以及如何使用 length、keys、has 和 map 函数,分别获取数组、字符串和对象的长度,获取对象键或数组索引,检查对象中是否存在键或数组是否有给定索引处的元素,并对数组的每个元素应用过滤器或函数。要了解 jq
的所有功能,请查看程序手册!