如何使用 jq 在 Linux 命令行上解析 JSON 文件

JSON 是用于在网络上传输基于文本的数据的最流行格式之一。它无处不在,你一定会遇到它。我们将向您展示如何使用 jq
命令从 Linux 命令行处理它。
JSON 和 jq
JSON 代表 JavaScript 对象表示法。这是一种允许以自描述方式将数据编码为纯文本文件的方案。 JSON 文件中没有注释——内容应该是不言自明的。每个数据值都有一个称为“名称”或“键”的文本字符串。这告诉您数据值是什么。它们一起被称为名称:值对,或键:值对。冒号 (:
) 将键与其值分开。
“对象”是键值对的集合。在 JSON 文件中,对象以左大括号 ({
) 开头,以右大括号 (}
) 结尾。 JSON 还支持“数组”,它们是有序的值列表。数组以左括号 ([
) 开头,以右括号 (]
) 结尾。
当然,从这些简单的定义中,可以产生任意的复杂性。例如,对象可以嵌套在对象中。对象可以包含数组,数组也可以包含对象。所有这些都可以有开放式的嵌套级别。
但在实践中,如果 JSON 数据的布局很复杂,数据布局的设计可能应该重新考虑。当然,如果您不生成 JSON 数据,只是尝试使用它,那么您对其布局没有发言权。不幸的是,在这些情况下,您只需要处理它。
大多数编程语言都有允许它们解析 JSON 数据的库或模块。遗憾的是,Bash shell 没有这样的功能。
需求是发明之母,jq
实用程序诞生了!使用 jq
,我们可以在 Bash shell 中轻松解析 JSON,甚至将 XML 转换为 JSON。无论您是必须使用精心设计的、优雅的 JSON,还是由噩梦构成的东西,都无关紧要。
如何安装jq
我们必须在用于研究本文的所有 Linux 发行版上安装 jq
。
要在 Ubuntu 上安装 jq
,请键入以下命令:
sudo apt-get install jq

要在 Fedora 上安装 jq
,请键入以下命令:
sudo dnf install jq

要在 Manjaro 上安装 jq
,请键入以下命令:
sudo pacman -Sy jq

如何使 JSON 可读
JSON 不关心空格,布局也不影响它。只要遵循 JSON 语法规则,处理 JSON 的系统就可以读取和理解它。正因为如此,JSON 通常作为一个简单的长字符串传输,而不考虑任何布局。这节省了一点空间,因为制表符、空格和换行符不必包含在 JSON 中。当然,所有这一切的缺点是当人们试图阅读它时。
让我们从 NASA 网站中提取一个简短的 JSON 对象,告诉我们国际空间站的位置。我们将使用 curl
,它可以下载文件来为我们检索 JSON 对象。
我们不关心 curl
通常生成的任何状态消息,因此我们将使用 -s
(无声)选项键入以下内容:
curl -s http://api.open-notify.org/iss-now.json

现在,稍加努力,您就可以阅读本文。您必须挑选出数据值,但这并不容易或不方便。让我们重复一遍,但这次我们将通过 jq
进行管道传输。
jq
使用过滤器来解析 JSON,这些过滤器中最简单的是句点 (.
),意思是“打印整个对象”。默认情况下,jq
漂亮地打印输出。
我们将它们放在一起并键入以下内容:
curl -s http://api.open-notify.org/iss-now.json | jq .

那好多了!现在,我们可以确切地看到发生了什么。
整个对象包裹在花括号中。它包含两个键:名称对:message
和 timestamp
。它还包含一个名为 iss_position
的对象,该对象包含两个键值对:longitude
和 latitude
。
我们会再试一次。这次我们将输入以下内容,并将输出重定向到一个名为“iss.json”的文件中:
curl -s http://api.open-notify.org/iss-now.json | jq . > iss.json
cat iss.json

这在我们的硬盘上为我们提供了一个布局良好的 JSON 对象副本。
访问数据值
正如我们在上面看到的,jq
可以从 JSON 中提取通过管道传输的数据值。它还可以处理存储在文件中的 JSON。我们将使用本地文件,这样命令行就不会被 curl
命令弄得乱七八糟。这应该使它更容易理解。
从 JSON 文件中提取数据的最简单方法是提供一个键名来获取其数据值。键入句点和键名,两者之间不要有空格。这会根据键名创建一个过滤器。我们还需要告诉 jq
使用哪个 JSON 文件。
我们键入以下内容以检索 message
值:
jq .message iss.json

jq
在终端窗口中打印 message
值的文本。
如果您的键名包含空格或标点符号,则必须将其过滤器用引号引起来。通常注意只使用字符、数字和下划线,这样 JSON 键名就不会出现问题。
首先,我们键入以下内容来检索 timestamp
值:
jq .timestamp iss.json

时间戳值被检索并打印在终端窗口中。
但是我们如何访问 iss_position
对象中的值呢?我们可以使用 JSON 点表示法。我们将在键值的“路径”中包含 iss_position
对象名称。为此,密钥所在对象的名称将位于密钥本身的名称之前。
我们键入以下内容,包括 latitude
键名(注意“.iss_position”和“.latitude”之间没有空格):
jq .iss_position.latitude iss.json

要提取多个值,您必须执行以下操作:
- 在命令行中列出键名。
- 用逗号分隔 (
,
)。 - 将它们括在引号 (
\
) 或撇号 () 中。
考虑到这一点,我们输入以下内容:
jq ".iss_position.latitude, .timestamp" iss.json

这两个值打印到终端窗口。
使用数组
让我们从 NASA 获取一个不同的 JSON 对象。
这一次,我们将使用目前在太空中的宇航员名单:
curl -s http://api.open-notify.org/astros.json

好的,那行得通,所以让我们再做一次。
我们将键入以下内容以通过 jq
将其通过管道传输并将其重定向到名为“astro.json”的文件:
curl -s http://api.open-notify.org/astros.json | jq . > astro.json

现在让我们输入以下内容来检查我们的文件:
less astro.json

如下图,我们现在看到了太空中的宇航员名单,以及他们乘坐的飞船。

此 JSON 对象包含一个名为 people
的数组。我们知道它是一个数组,因为左括号 ([
)(在上面的屏幕截图中突出显示)。它是一个对象数组,每个对象包含两个键值对:name
和 craft
。
就像我们之前所做的那样,我们可以使用 JSON 点表示法来访问这些值。我们还必须在数组名称中包含方括号 ([]
)。
考虑到所有这些,我们输入以下内容:
jq ".people[].name" astro.json

这一次,所有名称值都打印到终端窗口。我们要求 jq
做的是打印数组中每个对象的名称值。很整洁吧?
如果我们在命令行中将其在数组中的位置放在方括号 ([]
) 中,我们就可以检索单个对象的名称。该数组使用零偏移索引,这意味着数组第一个位置的对象为零。
要访问数组中的最后一个对象,您可以使用 -1;要获取数组中的倒数第二个对象,可以使用 -2,依此类推。
有时,JSON 对象会提供数组中元素的数量,这一个就是这种情况。除了数组之外,它还包含一个名为 number
的键:名称对,值为 6。
此数组中包含以下数量的对象:
jq ".people[1].name" astro.json
jq ".people[3].name" astro.json
jq ".people[-1].name" astro.json
jq ".people[-2].name" astro.json

您还可以在数组中提供开始和结束对象。这称为“切片”,可能有点令人困惑。请记住,数组使用零偏移量。
要从索引位置 2 检索对象,直到(但不包括)索引位置 4 的对象,我们键入以下命令:
jq ".people[2:4]" astro.json

这将打印数组索引二(数组中的第三个对象)和索引三(数组中的第四个对象)处的对象。它在数组索引 4 处停止处理,这是数组中的第五个对象。
更好地理解这一点的方法是在命令行上进行试验。您很快就会看到它是如何工作的。
如何使用带过滤器的管道
您可以将输出从一个过滤器传输到另一个过滤器,而不必学习新符号。与 Linux 命令行相同,jq
使用竖线 (|
) 表示管道。
我们将告诉 jq
将 people
数组通过管道传输到 .name
过滤器中,该过滤器应在终端窗口中列出宇航员的姓名。
我们键入以下内容:
jq ".people[] | .name" astro.json

创建数组和修改结果
我们可以使用 jq
来创建新的对象,比如数组。在此示例中,我们将提取三个值并创建一个包含这些值的新数组。请注意左括号 ([
) 和右括号 (]
) 也是过滤器字符串中的第一个和最后一个字符。
我们键入以下内容:
jq "[.iss-position.latitude, iss_position.longitude, .timestamp]" iss.json

输出包含在方括号中并用逗号分隔,使其成为一个格式正确的数组。
数值也可以在检索时进行操作。让我们从 ISS 位置文件中提取 timestamp
,然后再次提取它并更改返回的值。
为此,我们键入以下内容:
jq ".timestamp" iss.json
jq ".timestamp - 1570000000" iss.json

如果您需要在值数组中添加或删除标准偏移量,这将很有用。
让我们输入以下内容来提醒自己 iss.json
文件包含的内容:
jq . iss.json

假设我们想要摆脱 message
键值对。它与国际空间站的位置没有任何关系。它只是一个标志,表示位置已成功检索。如果它超出要求,我们可以免除它。 (你也可以忽略它。)
我们可以使用 jq
的删除函数 del()
来删除键值对。要删除消息键值对,我们键入以下命令:
jq "del(.message)" iss.json

请注意,这实际上并没有从“iss.json”文件中删除它;它只是将其从命令的输出中删除。如果您需要创建一个不包含 message
键值对的新文件,请运行该命令,然后将输出重定向到一个新文件中。
更复杂的 JSON 对象
让我们检索更多 NASA 数据。这一次,我们将使用一个 JSON 对象,其中包含来自世界各地的流星撞击地点的信息。这是一个更大的文件,具有比我们之前处理的文件复杂得多的 JSON 结构。
首先,我们将键入以下内容以将其重定向到名为“strikes.json”的文件:
curl -s https://data.nasa.gov/resource/y77d-th95.json | jq . > strikes.json

要查看 JSON 的外观,我们键入以下内容:
less strikes.json

如下所示,文件以左括号 ([
) 开头,因此整个对象是一个数组。数组中的对象是键值对的集合,并且有一个名为 geolocation
的嵌套对象。 geolocation
对象包含更多键值对,以及一个名为 coordinates
的数组。

让我们从索引位置 995 到数组末尾的对象中检索流星撞击的名称。
我们将键入以下内容以通过三个过滤器对 JSON 进行管道传输:
jq ".[995:] | .[] | .name" strikes.json

过滤器以下列方式发挥作用:
.[995:]
:这告诉jq
处理从数组索引 995 到数组末尾的对象。冒号后没有数字 (:
) 告诉jq
继续到数组的末尾。.[]
:这个数组迭代器告诉jq
处理数组中的每个对象。.name
:此过滤器提取名称值。
稍作改动,我们就可以从数组中提取最后 10 个对象。 “-10”指示 jq
开始处理数组末尾后 10 处的对象。
我们键入以下内容:
jq ".[-10:] | .[] | .name" strikes.json

正如我们在前面的示例中所做的那样,我们可以键入以下内容来选择单个对象:
jq ".[650].name" strikes.json

我们还可以对字符串应用切片。为此,我们将键入以下内容以请求数组索引 234 处对象名称的前四个字符:
jq ".[234].name[0:4]" strikes.json

我们还可以完整地看到一个特定的对象。为此,我们键入以下内容并包含一个没有任何键:值过滤器的数组索引:
jq ".[234]" strikes.json

如果只想查看值,可以在不查看键名的情况下执行相同的操作。
对于我们的示例,我们键入以下命令:
jq ".[234][]" strikes.json

要从每个对象中检索多个值,我们在以下命令中用逗号分隔它们:
jq ".[450:455] | .[] | .name, .mass" strikes.json

如果要检索嵌套值,则必须标识构成它们“路径”的对象。
例如,要引用 coordinates
值,我们必须包含包罗万象的数组、geolocation
嵌套对象和嵌套的 coordinates
数组, 如下所示。

要查看数组索引位置 121 处对象的 坐标
值,我们键入以下命令:
jq ".[121].geolocation.coordinates[]" strikes.json

长度函数
jq
length
函数根据应用的内容给出不同的指标,例如:
- Strings:字符串的长度,以字节为单位。
- 对象:对象中键值对的数量。
- Arrays:数组中数组元素的个数。
以下命令返回 JSON 数组中 10 个对象中 name
值的长度,从索引位置 100 开始:
jq ".[100:110] | .[].name | length" strikes.json

要查看数组中第一个对象中有多少键值对,我们键入以下命令:
jq ".[0] | length" strikes.json

按键功能
您可以使用 keys 函数来了解您要使用的 JSON。它可以告诉您键的名称是什么,以及数组中有多少个对象。
要在“astro.json”文件的 people
对象中查找键,我们键入以下命令:
jq ".people.[0] | keys" astro.json

要查看 people
数组中有多少元素,我们键入以下命令:
jq ".people | keys" astro.json

这表明有六个零偏移数组元素,编号为零到五。
has() 函数
您可以使用 has()
函数来查询 JSON 并查看对象是否具有特定的键名。请注意,键名必须用引号引起来。我们将过滤器命令用单引号 () 括起来,如下所示:
jq '.[] | has("nametype")' strikes.json

数组中的每个对象都被选中,如下所示。

如果要检查特定对象,请将其索引位置包含在数组过滤器中,如下所示:
jq '.[678] | has("nametype")' strikes.json

没有它就不要靠近 JSON
jq
实用程序是专业、强大、快速的软件的完美示例,它让 Linux 世界的生活变得如此愉快。
这只是对该命令的常用功能的简要介绍——还有很多。如果您想深入了解,请务必查看全面的 jq 手册。
Linux Commands | ||
Files | tar · pv · cat · tac · chmod · grep · diff · sed · ar · man · pushd · popd · fsck · testdisk · seq · fd · pandoc · cd · $PATH · awk · join · jq · fold · uniq · journalctl · tail · stat · ls · fstab · echo · less · chgrp · chown · rev · look · strings · type · rename · zip · unzip · mount · umount · install · fdisk · mkfs · rm · rmdir · rsync · df · gpg · vi · nano · mkdir · du · ln · patch · convert · rclone · shred · srm · scp · gzip · chattr · cut · find · umask · wc | |
Processes | alias · screen · top · nice · renice · progress · strace · systemd · tmux · chsh · history · at · batch · free · which · dmesg · chfn · usermod · ps · chroot · xargs · tty · pinky · lsof · vmstat · timeout · wall · yes · kill · sleep · sudo · su · time · groupadd · usermod · groups · lshw · shutdown · reboot · halt · poweroff · passwd · lscpu · crontab · date · bg · fg · pidof · nohup · pmap | |
Networking | netstat · ping · traceroute · ip · ss · whois · fail2ban · bmon · dig · finger · nmap · ftp · curl · wget · who · whoami · w · iptables · ssh-keygen · ufw · arping · firewalld |
RELATED: Best Linux Laptops for Developers and Enthusiasts