jq 是一个轻量级且灵活的命令行 JSON 处理器。
jq 就像sed
JSON 数据一样 - 您可以使用它来切片、过滤、映射和转换结构化数据,就像 ,sed
和 朋友让您玩文本一样容易。awkgrep
jq 是用可移植的 C 语言编写的,它具有零运行时依赖性。您可以下载单个二进制文件,scp
将其下载到同一类型的遥远机器上,并期望它能够正常工作。
jq 可以毫不费力地将您拥有的数据格式转换为您想要的格式,并且执行此操作的程序通常比您预期的更短更简单。
快速开始之helloworld
1. 美化输出Json:
代码语言:javascript复制echo '{ "foo": { "bar": { "baz": 123 } } }' | jq '.'
{
"foo": {
"bar": {
"baz": 123
}
}
}
2. 只输入部分字段
代码语言:javascript复制echo '{"foo": 42, "bar": "less interesting data"}' | jq '.foo'
42
cli 参数
jq 过滤器在 JSON 数据流上运行。jq 的输入被解析为一系列以空格分隔的 JSON 值,一次一个地通过提供的过滤器。过滤器的输出被写入标准输出,同样是一系列以空格分隔的 JSON 数据。
注意:注意 shell 的引用规则很重要。作为一般规则,最好总是引用(使用单引号字符) jq 程序,因为太多对 jq 具有特殊含义的字符也是 shell 元字符。例如,jq "foo"
在大多数 Unix shell 上会失败,因为这与 相同jq foo
,通常会失败,因为foo is not defined
. 使用 Windows 命令 shell (cmd.exe) 时,最好在命令行中给出 jq 程序时使用双引号(而不是-f program-file
选项),但是 jq 程序中的双引号需要反斜杠转义。
您可以使用一些命令行选项影响 jq 如何读取和写入其输入和输出:
--version
:
输出 jq 版本并以零退出。
--seq
:
使用application/json-seq
MIME 类型方案在 jq 的输入和输出中分隔 JSON 文本。这意味着在输出的每个值之前打印一个 ASCII RS(记录分隔符)字符,并且在每个输出之后打印一个 ASCII LF(换行符)。无法解析的输入 JSON 文本将被忽略(但警告),丢弃所有后续输入,直到下一个 RS。此模式还解析 jq 的输出而不使用该--seq
选项。
--stream
:
以流方式解析输入,输出路径和叶值数组(标量和空数组或空对象)。例如,"a"
变成[[],"a"]
,[[],"a",["b"]]
变成[[0],[]]
,[[1],"a"]
,和[[1,0],"b"]
。
这对于处理非常大的输入很有用。将此与过滤和reduce
andforeach
语法结合使用,以逐步减少大输入。
--slurp
/-s
:
不要为输入中的每个 JSON 对象运行过滤器,而是将整个输入流读入一个大数组并只运行一次过滤器。
--raw-input
/-R
:
不要将输入解析为 JSON。相反,每一行文本都作为字符串传递给过滤器。如果与 结合使用--slurp
,则整个输入将作为单个长字符串传递给过滤器。
--null-input
/-n
:
根本不读取任何输入!相反,过滤器null
作为输入运行一次。这在将 jq 用作简单的计算器或从头构建 JSON 数据时很有用。
--compact-output
/-c
:
默认情况下,jq 漂亮地打印 JSON 输出。使用此选项将通过将每个 JSON 对象放在一行中来生成更紧凑的输出。
--tab
:
每个缩进级别使用一个制表符,而不是两个空格。
--indent n
:
使用给定数量的空格(不超过 7 个)进行缩进。
--color-output
/-C
和--monochrome-output
/-M
:
默认情况下,如果写入终端,jq 会输出彩色 JSON。即使使用 写入管道或文件,您也可以强制它产生颜色-C
,并使用 禁用颜色-M
。
可以使用JQ_COLORS
环境变量配置颜色(见下文)。
--binary
/-b
:
使用 WSL、MSYS2 或 Cygwin 的 Windows 用户在使用本机 jq.exe 时应使用此选项,否则 jq 会将换行符 (LF) 转换为回车换行符 (CRLF)。
--ascii-output
/-a
:
jq 通常将非 ASCII Unicode 代码点输出为 UTF-8,即使输入将它们指定为转义序列(如“u03bc”)。使用此选项,您可以强制 jq 生成纯 ASCII 输出,并将每个非 ASCII 字符替换为等效的转义序列。
--unbuffered
:
在打印每个 JSON 对象后刷新输出(如果您将慢速数据源传送到 jq 并将 jq 的输出传送到其他地方,这很有用)。
--sort-keys
/-S
:
按排序顺序输出每个对象的字段和键。
--raw-output
/-r
:
使用此选项,如果过滤器的结果是字符串,那么它将直接写入标准输出,而不是格式化为带引号的 JSON 字符串。这对于使 jq 过滤器与非基于 JSON 的系统对话很有用。
--join-output
/-j
:
Like-r
但 jq 不会在每次输出后打印换行符。
--nul-output
/-0
:
像-r
,但 jq 将在每次输出后打印 NUL 而不是换行符。当输出的值可以包含换行符时,这可能很有用。
-f filename
/--from-file filename
:
从文件而不是从命令行读取过滤器,如 awk 的 -f 选项。您也可以使用“#”来发表评论。
-Ldirectory
/-L directory
:
预先directory
添加到模块的搜索列表中。如果使用此选项,则不使用内置搜索列表。请参阅下面的模块部分。
-e
/--exit-status
:
false
如果最后一个输出值不是nor null
,则将jq 的退出状态设置为 0,如果最后一个输出值是false
or null
,则设置为 1,如果没有产生有效结果,则设置为 4。如果有任何使用问题或系统错误,jq 通常退出 2,如果有 jq 程序编译错误,则退出 3,或者如果 jq 程序运行,则退出 0。
另一种设置退出状态的方法是使用halt_error
内置函数。
--arg name value
:
此选项将值作为预定义变量传递给 jq 程序。如果您使用 运行 jq --arg foo bar,则foo在程序中可用并具有值"bar"。请注意, value将被视为字符串,因此--arg foo 123将绑定
jq 程序也可以使用命名参数作为 $ARGS.named
.
--argjson name JSON-text
:
此选项将 JSON 编码的值作为预定义变量传递给 jq 程序。如果您使用 运行 jq --argjson foo 123
,则 $foo
在程序中可用并具有值123
。
--slurpfile variable-name filename
:
此选项读取命名文件中的所有 JSON 文本,并将解析的 JSON 值的数组绑定到给定的全局变量。如果您使用 运行 jq --slurpfile foo bar
,则$foo
在程序中可用,并且有一个数组,其元素对应于名为 的文件中的文本bar
。
--rawfile variable-name filename
:
此选项读入命名文件并将其内容绑定到给定的全局变量。如果你运行 jq with --rawfile foo bar
, then$foo
在程序中是可用的,并且有一个字符串,其内容是文件中的 texs 名为bar
.
--argfile variable-name filename
:
不使用。改为使用--slurpfile
。
(此选项类似于--slurpfile
,但当文件只有一个文本时,则使用该文本,否则使用文本数组,如--slurpfile
。)
--args
:
其余参数是位置字符串参数。这些可用于 jq 程序作为$ARGS.positional[]
.
--jsonargs
:
其余参数是位置 JSON 文本参数。这些可用于 jq 程序作为$ARGS.positional[]
.
--run-tests [filename]
:
在给定文件或标准输入中运行测试。这必须是给出的最后一个选项,并且不支持所有前面的选项。输入由注释行、空行和程序行组成,后跟一个输入行,与预期一样多的输出行(每个输出一个),以及一个终止空行。编译失败测试从仅包含“%�IL”的行开始,然后是包含要编译的程序的行,然后是包含要与实际进行比较的错误消息的行。
请注意,此选项可能会向后不兼容地更改。
基本过滤器
占位符:.
绝对最简单的过滤器是.
. 这是一个过滤器,它接受其输入并将其作为输出不变地产生。也就是说,这是占位运算符。
由于 jq 默认情况下会漂亮地打印所有输出,因此这个简单的程序可以成为格式化 JSON 输出的有用方法,例如curl
.
对象标识符索引:.foo
,.foo.bar
最简单有用的过滤器是.foo
. 当给定一个 JSON 对象(又名字典或哈希)作为输入时,它会在键“foo”处生成值,如果不存在则为 null。
形式的过滤器.foo.bar
等价于.foo|.bar
。
此语法仅适用于简单的类似标识符的键,即全部由字母数字字符和下划线组成且不以数字开头的键。
如果键包含特殊字符或以数字开头,则需要用双引号将其括起来,例如: ."foo
例如.["foo::bar"]
and .["foo.bar"]
work while .foo::bar
doesn't, and.foo.bar
意味着.["foo"].["bar"]
。
可选对象标识符索引:.foo?
就像, 但在 不是数组或对象.foo
时甚至不输出错误。.
通用对象索引:.[<string>]
您还可以使用类似这样的语法查找对象的字段 .["foo"]
(上面的 .foo 是它的简写版本,但仅适用于类似标识符的字符串)。
数组索引:.[2]
当索引值为整数时,.[<value>]
可以索引数组。数组从零开始,因此.[2]
返回第三个元素。
允许使用负索引,-1 表示最后一个元素,-2 表示倒数第二个元素,依此类推。
数组/字符串切片:.[10:15]
该.[10:15]
语法可用于返回数组的子数组或字符串的子字符串。返回的数组 .[10:15]
长度为 5,包含从索引 10(包括)到索引 15(不包括)的元素。任何一个索引都可以是负数(在这种情况下,它从数组的末尾向后计数),或者被省略(在这种情况下,它指的是数组的开头或结尾)。
数组/对象值迭代器:.[]
如果使用.[index]
语法,但完全省略索引,它将返回数组的所有元素。.[]
使用输入运行[1,2,3]
将产生三个单独的结果,而不是单个数组。
您也可以在对象上使用它,它将返回对象的所有值。
.[]?
类似.[]
,但如果 . 则不会输出错误。不是数组或对象。
逗号:,
如果两个过滤器用逗号分隔,那么相同的输入将被馈送到两个过滤器,两个过滤器的输出值流将按顺序连接:首先,左表达式产生的所有输出,然后是所有输出由权利产生。例如, filter.foo, .bar
生成“foo”字段和“bar”字段作为单独的输出。
管道:|
该| 运算符通过将左侧一个的输出馈送到右侧一个的输入来组合两个过滤器。如果您习惯的话,它与 Unix shell 的管道几乎相同。
如果左边的那个产生多个结果,那么右边的那个将为每个结果运行。因此,表达式.[] | .foo
检索输入数组的每个元素的“foo”字段。
请注意,.a.b.c
与 相同.a | .b | .c
。
还要注意,这.
是“管道”中特定阶段的输入值,特别是:.
表达式出现的位置。因此.a | . | .b
与 相同.a.b
,因为.
中间的 指的是.a
产生的任何值。
插入语
就像在任何典型的编程语言中一样,括号作为分组运算符工作。
类型和值
jq 支持与 JSON 相同的数据类型集 - 数字、字符串、布尔值、数组、对象(在 JSON 中是只有字符串键的散列)和“null”。
布尔值、空值、字符串和数字的编写方式与 javascript 相同。就像 jq 中的其他所有内容一样,这些简单的值接受一个输入并产生一个输出 -42
是一个有效的 jq 表达式,它接受一个输入,忽略它,并返回 42。
数组构造:[]
在 JSON 中,[]
用于构造数组,如在 [1,2,3]
. 数组的元素可以是任何 jq 表达式,包括管道。所有表达式产生的所有结果都收集到一个大数组中。您可以使用它从已知数量的值中构造一个数组(如[.foo, .bar, .baz]
)或将过滤器的所有结果“收集”到一个数组中(如[.items[].name]
)
一旦你理解了 "," 操作符,你就可以从不同的角度来看待 jq 的数组语法:表达式[1,2,3]
没有使用逗号分隔数组的内置语法,而是将[]
操作符(收集结果)应用于表达式1,2,3(产生三种不同的结果)。
如果您有一个X
产生四个结果的过滤器,那么表达式[X]
将产生一个结果,即一个由四个元素组成的数组。
对象构造:{}
像 JSON 一样,{}
用于构造对象(又名字典或哈希),如:{"a": 42, "b": 17}
.
如果键是“类似标识符”,则可以省略引号,如{a:42, b:17}
. 作为键表达式的变量引用使用变量的值作为键。常量文字、标识符或变量引用以外的关键表达式需要用括号括起来,例如 {("a" "b"):59}
.
该值可以是任何表达式(尽管您可能需要将其括在括号中,例如,如果它包含冒号),它将应用于 {} 表达式的输入(请记住,所有过滤器都有一个输入和一个输出)。
代码语言:javascript复制{foo: .bar}
{"foo": 42}
如果将 JSON 对象{"bar":42, "baz":43}
作为其输入,将生成 JSON 对象。您可以使用它来选择对象的特定字段:如果输入是具有“user”、“title”、“id”和“content”字段的对象,而您只需要“user”和“title”,则可以写
{user: .user, title: .title}
因为这很常见,所以有一个快捷语法: {user, title}
.
如果其中一个表达式产生多个结果,则将产生多个字典。如果输入的
代码语言:javascript复制{"user":"stedolan","titles":["JQ Primer", "More JQ"]}
然后表达式
代码语言:javascript复制{user, title: .titles[]}
将产生两个输出:
代码语言:javascript复制{"user":"stedolan", "title": "JQ Primer"}
{"user":"stedolan", "title": "More JQ"}
在键周围加上括号意味着它将被评估为表达式。使用与上述相同的输入,
代码语言:javascript复制{(.user): .titles}
输出
代码语言:javascript复制{"stedolan": ["JQ Primer", "More JQ"]}
作为键的变量引用使用变量的值作为键。如果没有值,则变量的名称将成为键,其值将成为值,
代码语言:javascript复制"f o o" as $foo | "b a r" as $bar | {$foo, $bar:$foo}
输出
代码语言:javascript复制{"f o o":"f o o","b a r":"f o o"}
递归下降:..
递归下降.
,产生每个值。这与内置的零参数相同recurse
(见下文)。这旨在类似于 XPath//
运算符。注意 ..a
不起作用;改为使用..|.a
。在下面的示例中,我们使用..|.a?
在 "below" 找到的任何对象中查找对象键 "a" 的所有值.
。
这在与path(EXP)
(另见下文)和?
运算符结合使用时特别有用。
内置运算符和函数
一些 jq 运算符(例如
)根据其参数的类型(数组、数字等)执行不同的操作。但是, jq 从不进行隐式类型转换。如果您尝试将字符串添加到对象,您将收到一条错误消息并且没有结果。
添加:
运算符
采用两个过滤器,将它们都应用于相同的输入,然后将结果相加。“添加”的含义取决于所涉及的类型:
- 数字是通过普通算术相加的。
- 数组是通过连接成一个更大的数组来添加的。
- 通过加入更大的字符串来添加字符串。
- 通过合并添加对象,即将两个对象中的所有键值对插入到单个组合对象中。如果两个对象都包含相同键的值,则右侧的对象
*
运算符。)
null
可以添加到任何值,并返回其他值不变。
减法:-
除了对数字进行普通算术减法外,该-
运算符还可用于数组以从第一个数组中删除第二个数组元素的所有出现。
乘法、除法、取模:*
, /
, 和%
当给定两个数字时,这些中缀运算符的行为符合预期。除以零会引发错误。x % y
计算 x 模 y。
将一个字符串乘以一个数字会产生多次串联该字符串。"x" * 0
产生null。
将一个字符串除以另一个字符串会使用第二个字符串作为分隔符来拆分第一个字符串。
将两个对象相乘将递归合并它们:这类似于加法,但如果两个对象都包含相同键的值,并且值是对象,则两者将使用相同的策略合并。
length
内置函数length
获取各种不同类型值的长度:
- 字符串的长度是它包含的 Unicode 代码点的数量(如果它是纯 ASCII,它将与它的 JSON 编码长度(以字节为单位)相同)。
- 数组的长度是元素的数量。
- 对象的长度是键值对的数量。
- null的长度为零。
utf8bytelength
内置函数utf8bytelength
输出用于以 UTF-8 编码字符串的字节数。
keys
,keys_unsorted
内置函数keys
,当给定一个对象时,会在一个数组中返回它的键。
键按 unicode 代码点顺序“按字母顺序”排序。这不是在任何特定语言中都特别有意义的顺序,但您可以指望它对于具有相同键集的任何两个对象都是相同的,而不管区域设置如何。
当keys
给定一个数组时,它返回该数组的有效索引:从 0 到 length-1 的整数。
该keys_unsorted
函数就像keys
,但如果输入是一个对象,那么键将不会被排序,而是键将大致按插入顺序排列。
has(key)
内置函数has
返回输入对象是否具有给定键,或者输入数组在给定索引处是否具有元素。
has(key)key 与检查是否是由 返回的数组的成员具有相同的效果keys,但has 会更快。
in
内置函数in
返回输入键是否在给定对象中,或者输入索引是否对应于给定数组中的元素。它本质上是has
.
map(x)
,map_values(x)
对于任何 filter x
,map(x)
将为输入数组的每个元素运行该过滤器,并在新数组中返回输出。map(. 1)
将递增数字数组的每个元素。
同样,map_values(x)
将为每个元素运行该过滤器,但是当传递一个对象时它将返回一个对象。
map(x)
相当于[.[] | x]
。事实上,这就是它的定义方式。同样,map_values(x)
定义为.[] |= x
。
path(path_expression)
输出给定路径表达式的数组表示形式.
。输出是字符串数组(对象键)和/或数字(数组索引)。
路径表达式是 jq 表达式,例如.a
, 也是.[]
. 有两种类型的路径表达式:一种可以完全匹配,另一种不能。例如,.a.b.c
is 是一个完全匹配的路径表达式,while.a[].b
不是。
path(exact_path_expression)
将产生路径表达式的数组表示,即使它不存在于.
, if .
isnull
或数组或对象中。
path(pattern)
如果路径pattern
存在于.
.
请注意,路径表达式与普通表达式没有区别。该表达式 path(..|select(type=="boolean"))
输出 中布尔值的所有路径.
,并且仅输出那些路径。
del(path_expression)
内置函数del
从对象中删除键及其对应的值。
getpath(PATHS)
内置函数getpath
输出在 中.
的每个路径中找到的值PATHS
。
setpath(PATHS; VALUE)
内置函数setpath
将PATHS
in设置.
为VALUE
.
delpaths(PATHS)
内置函数delpaths
设置PATHS
in .
。 PATHS
必须是路径数组,其中每个路径都是字符串和数字的数组。
to_entries
, from_entries
,with_entries
这些函数在对象和键值对数组之间进行转换。如果to_entries
传递了一个对象,那么对于k: v
输入中的每个条目,输出数组包括{"key": k, "value": v}
.
from_entries
进行相反的转换,并且 with_entries(foo)
是 的简写to_entries | map(foo) | from_entries
,用于对对象的所有键和值进行某些操作。from_entries
接受键、键、名称、名称、值和值作为键。
select(boolean_expression)
如果对该输入返回 true,则该函数select(foo)
将产生其输入不变 foo
,否则不产生任何输出。
它对过滤列表很有用:[1,2,3] | map(select(. >= 2))
会给你[2,3]
.
arrays
, objects
, iterables
, booleans
, numbers
, normals
, finites
, strings
, nulls
, values
,scalars
这些内置函数分别只选择数组、对象、可迭代对象(数组或对象)、布尔值、数字、普通数、有限数、字符串、空值、非空值和非可迭代值的输入。
empty
empty
不返回任何结果。一个都没有。甚至没有null
。
有时它很有用。你会知道你是否需要它:)
error(message)
产生错误,就像.a
应用于 null 和对象以外的值一样,但将给定的消息作为错误的值。可以使用 try/catch 捕获错误;见下文。
halt
停止 jq 程序,不再输出。jq 将以退出状态退出0
。
halt_error
,halt_error(exit_code)
停止 jq 程序,不再输出。输入将stderr
作为原始输出(即字符串没有双引号)打印,没有任何装饰,甚至没有换行符。
给定的exit_code
(默认为5
)将是 jq 的退出状态。
例如,"Error: somthing went wrongn"|halt_error(1)
。
$__loc__
生成一个带有“file”键和“line”键的对象,其中$__loc__
出现的文件名和行号作为值。
paths
, paths(node_filter)
,leaf_paths
paths
输出其输入中所有元素的路径(除了它不输出空列表,表示 . 本身)。
paths(f)
输出任何f
为真值的路径。也就是说,paths(numbers)
输出所有数值的路径。
leaf_paths
是paths(scalars)
;的别名 leaf_paths
已 弃用,将在下一个主要版本中删除。
add
过滤器add
将一个数组作为输入,并将数组的元素加在一起作为输出。这可能意味着求和、连接或合并,具体取决于输入数组元素的类型 - 规则与运算符的规则相同
(如上所述)。
如果输入是空数组,则add
返回null
。
any
, any(condition)
,any(generator; condition)
过滤器any
将布尔值数组作为输入,true
如果数组的任何元素为 ,则将其作为输出true
。
如果输入是空数组,则any
返回false
。
该any(condition)
表单将给定条件应用于输入数组的元素。
该any(generator; condition)
表格将给定条件应用于给定生成器的所有输出。
all
, all(condition)
,all(generator; condition)
过滤器all
将一个布尔值数组作为输入,true
如果数组的所有元素都是 ,则将其作为输出true
。
该all(condition)
表单将给定条件应用于输入数组的元素。
该all(generator; condition)
表格将给定条件应用于给定生成器的所有输出。
如果输入是空数组,则all
返回true
。
flatten
,flatten(depth)
过滤器flatten
将嵌套数组的数组作为输入,并生成一个平面数组,其中原始数组中的所有数组都已被其值递归替换。您可以将参数传递给它以指定要展平的嵌套级别。
flatten(2)
就像flatten
,但只有两个级别的深度。
range(upto)
,range(from;upto)
range(from;upto;by)
该range
函数产生一系列数字。range(4;10)
产生 6 个数字,从 4(包括)到 10(不包括)。这些数字作为单独的输出产生。用于[range(4;10)]
将范围作为数组获取。
one 参数形式生成从 0 到给定数字的数字,增量为 1。
两个参数形式生成从from
到upto
以 1 为增量的数字。
三自变量形式生成from
以upto
为增量的数字by
。
floor
该floor
函数返回其数字输入的下限。
sqrt
该sqrt
函数返回其数字输入的平方根。
tonumber
该tonumber
函数将其输入解析为数字。它会将格式正确的字符串转换为等效的数字,不理会数字,并在所有其他输入上出错。
tostring
该tostring
函数将其输入打印为字符串。字符串保持不变,所有其他值都是 JSON 编码的。
type
该type
函数将其参数的类型作为字符串返回,它是 null、布尔值、数字、字符串、数组或对象之一。
infinite
, nan
, isinfinite
, isnan
, isfinite
,isnormal
一些算术运算可以产生无穷大和“非数字”(NaN)值。 如果其输入是无限的,则isinfinite
内置函数返回。true
如果其 输入是 NaN ,则isnan
内置函数返回。内置函数返回一个正无穷大值true
。内置函数返回一个 NaN infinite
。nan
如果其isnormal
输入是正常数字,则内置函数返回 true。
请注意,除以零会引发错误。
目前,大多数在无穷大、NaN 和次正规数上进行的算术运算都不会引发错误。
sort, sort_by(path_expression)
这些sort
函数对其输入进行排序,该输入必须是一个数组。值按以下顺序排序:
null
false
true
- 数字
- 字符串,按字母顺序(按 unicode 代码点值)
- 数组,按词法顺序
- 对象
对象的排序有点复杂:首先通过比较它们的键集(作为排序顺序的数组)来比较它们,如果它们的键相等,则逐个键比较值。
sort
可用于按对象的特定字段或应用任何 jq 过滤器进行排序。
sort_by(foo)
通过比较每个元素的结果来比较两个元素 foo
。
group_by(path_expression)
group_by(.foo)
将数组作为输入,将具有相同.foo
字段的元素分组到单独的数组中,并将所有这些数组生成为更大数组的元素,并按.foo
字段的值排序。
任何 jq 表达式,不仅仅是一个字段访问,都可以用来代替.foo
. sort
排序顺序与上面函数中描述的相同。
min
, max
, min_by(path_exp)
,max_by(path_exp)
查找输入数组的最小或最大元素。
和函数允许您指定要检查的特定字段或属性,例如 min_by(path_exp)
查找具有最小字段的对象。max_by(path_exp)min_by(.foo)foo
unique
,unique_by(path_exp)
该unique
函数将一个数组作为输入,并按排序顺序生成一个包含相同元素的数组,并删除重复项。
对于通过应用参数获得的每个值,该unique_by(path_exp)
函数将只保留一个元素。可以将其视为通过从group
.
reverse
此函数反转数组。
contains(element)
contains(b)
如果 b 完全包含在输入中,则过滤器将产生 true。如果 B 是 A 的子字符串,则字符串 B 包含在字符串 A 中。如果 B 中的所有元素都包含在 A 中的任何元素中,则数组 B 包含在数组 A 中。如果所有元素都包含在对象 B 中,则对象 B 包含在对象 A 中B 中的值包含在具有相同键的 A 中的值中。如果所有其他类型相等,则假定它们相互包含。
indices(s)
输出一个数组,其中包含.
where的索引s
。输入可能是一个数组,在这种情况下,如果s
是一个数组,那么索引输出将是所有元素.
匹配的那些s
。
index(s)
,rindex(s)
index
输出输入中第一个 ( ) 或最后一个 ( rindex
) 出现的索引s
。
inside
inside(b)
如果输入完全包含在 b 中,则过滤器将产生 true。它本质上是contains
.
startswith(str)
输出true
如果。从给定的字符串参数开始。
endswith(str)
输出true
如果。以给定的字符串参数结束。
combinations
,combinations(n)
输出输入数组中数组元素的所有组合。如果给定一个参数n
,它会输出n
输入数组的所有重复组合。
ltrimstr(str)
如果它以它开头,则输出它的输入并删除给定的前缀字符串。
rtrimstr(str)
如果它以它结尾,则输出它的输入并删除给定的后缀字符串。
explode
将输入字符串转换为字符串代码点编号的数组。
implode
爆炸的反面。
split(str)
在分隔符参数上拆分输入字符串。
join(str)
使用参数作为分隔符连接作为输入给定的元素数组。它是split
: 的倒数,也就是说,split("foo") | join("foo")
在任何输入字符串上运行都会返回所述输入字符串。
输入中的数字和布尔值被转换为字符串。Null 值被视为空字符串。不支持输入中的数组和对象。
ascii_downcase
,ascii_upcase
发出输入字符串的副本,并将其字母字符(az 和 AZ)转换为指定的大小写。
while(cond; update)
该while(cond; update)
功能允许您重复应用更新,.
直到cond
为假。
请注意,它while(cond; update)
在内部定义为递归 jq 函数。如果每个输入最多产生一个输出,则内部的递归调用while
不会消耗额外的内存。update
请参阅下面的高级主题。
until(cond; next)
该until(cond; next)
函数允许您重复应用表达式next
,从最初到.
然后到它自己的输出,直到cond
为真。例如,这可用于实现阶乘函数(见下文)。
请注意,它until(cond; next)
在内部定义为递归 jq 函数。如果每个输入最多产生一个输出,则内部的递归调用until()
不会消耗额外的内存。next
请参阅下面的高级主题。
recurse(f)
, recurse
, recurse(f; condition)
,recurse_down
该recurse(f)
功能允许您搜索递归结构,并从各个级别提取有趣的数据。假设您的输入代表一个文件系统:
{"name": "/", "children": [
{"name": "/bin", "children": [
{"name": "/bin/ls", "children": []},
{"name": "/bin/sh", "children": []}]},
{"name": "/home", "children": [
{"name": "/home/stephen", "children": [
{"name": "/home/stephen/jq", "children": []}]}]}]}
现在假设您要提取所有存在的文件名。您需要检索.name
、.children[].name
、 .children[].children[].name
等。你可以这样做:
recurse(.children[]) | .name
不带参数调用时,recurse
等效于 recurse(.[]?)
.
recurse(f)
与递归深度相同recurse(f; . != null)
,可以在不考虑递归深度的情况下使用。
recurse(f; condition)
是一个以发射 开始的生成器。然后依次发出 .|f, .|f|f, .|f|f|f, ... 只要计算值满足条件。例如,要生成所有整数,至少原则上可以写成recurse(. 1; true)
.
由于遗留原因,作为不带参数recurse_down
调用的别名存在。recurse
此别名已被 弃用,将在下一个主要版本中删除。
只要每个输入最多产生一个输出,递归调用recurse
就不会消耗额外的内存。f
walk(f)
该walk(f)
函数递归地应用于输入实体的每个组件。当遇到一个数组时,f首先应用于其元素,然后应用于数组本身;当遇到一个对象时,首先将 f 应用于所有值,然后再应用于该对象。在实践中,f 通常会测试其输入的类型,如下面的示例所示。第一个示例强调了在处理数组本身之前处理数组元素的有用性。第二个示例显示了如何考虑更改输入中所有对象的所有键。
$ENV
,env
$ENV
是一个对象,表示 jq 程序启动时设置的环境变量。
env
输出一个代表 jq 当前环境的对象。
目前没有用于设置环境变量的内置函数。
transpose
转置一个可能锯齿状的矩阵(数组的数组)。行用空值填充,因此结果始终为矩形。
bsearch(x)
bsearch(x) 在输入数组中对 x 进行二分搜索。如果输入已排序并包含 x,则 bsearch(x) 将返回其在数组中的索引;否则,如果数组已排序,它将返回 (-1 - ix),其中 ix 是一个插入点,因此在将 x 插入到 ix 后,该数组仍将被排序。如果数组未排序,bsearch(x) 将返回一个可能不感兴趣的整数。
字符串插值 -(foo)
在字符串中,您可以在反斜杠后的括号内放置表达式。无论表达式返回什么,都将被插入到字符串中。
转换为/从 JSON
tojson
和builtins 分别将值转储为 JSON 文本或将fromjson
JSON 文本解析为值。内置 tojson 与 tostring 的不同之处在于 tostring 返回未修改的字符串,而 tojson 将字符串编码为 JSON 字符串。
格式化字符串和转义
该@foo
语法用于格式化和转义字符串,这对于构建 URL、HTML 或 XML 等语言的文档等很有用。@foo
可以单独用作过滤器,可能的转义是:
@text
:
调用tostring
,请参阅该函数以获取详细信息。
@json
:
将输入序列化为 JSON。
@html
:
通过将字符映射 <>&'"
到它们的实体等价物<
, >
, &
, '
,来应用 HTML/XML 转义"
。
@uri
:
通过将所有保留的 URI 字符映射到一个%XX
序列来应用百分比编码。
@csv
:
输入必须是一个数组,并将其呈现为 CSV,字符串带有双引号,引号通过重复转义。
@tsv
:
输入必须是一个数组,并且呈现为 TSV(制表符分隔值)。每个输入数组将打印为一行。字段由单个选项卡 (ascii 0x09
) 分隔。输入字符换行 (ascii 0x0a
)、回车 (ascii 0x0d
)、制表符 (ascii 0x09
) 和反斜杠 (ascii 0x5c
) 将分别输出为转义 序列n
, r
, 。t\
@sh
:
输入经过转义,适合在 POSIX shell 的命令行中使用。如果输入是数组,则输出将是一系列以空格分隔的字符串。
@base64
:
输入将转换为 RFC 4648 指定的 base64。
@base64d
:
, 的逆@base64
输入按照 RFC 4648 的规定进行解码。 注意:如果解码的字符串不是 UTF-8,则结果未定义。
这种语法可以以一种有用的方式与字符串插值相结合。您可以在@foo
标记后面加上字符串文字。字符串文字的内容不会被转义。但是,在该字符串文字内进行的所有插值都将被转义。例如,
@uri "https://www.google.com/search?q=(.search)"
将为输入产生以下输出 {"search":"what is jq?"}
:
"https://www.google.com/search?q=what is jq?"
请注意,URL 中的斜杠、问号等不会被转义,因为它们是字符串文字的一部分。
日期
jq 提供了一些基本的日期处理功能,以及一些高级和低级的内置函数。在所有情况下,这些内置函数都专门处理 UTC 时间。
内置函数将 ISO 8601 格式的fromdateiso8601
日期时间解析为自 Unix 纪元 (1970-01-01T00:00:00Z) 以来的秒数。todateiso8601
内置执行相反的操作。
fromdate
内置解析日期时间字符串。目前 fromdate
仅支持 ISO 8601 日期时间字符串,但未来它将尝试解析更多格式的日期时间字符串。
内置函数todate
是.todateiso8601
now
内置输出当前时间,以 Unix 纪元以来的秒数为单位。
还提供了 C 库时间函数的低级 jq 接口:strptime
、strftime
、strflocaltime
、 mktime
、gmtime
和localtime
. strptime
有关和使用的格式字符串,请参阅主机操作系统的文档strftime
。注意:这些不一定是 jq 中的稳定接口,尤其是它们的本地化功能。
内置函数消耗自 Unix 纪元以来的gmtime
秒数,并输出 Greenwhich Meridian 时间的“分解时间”表示,作为表示(按此顺序)的数字数组:年、月(从零开始)、日期月份(从 1 开始)、一天中的小时、小时中的分钟、分钟中的秒、一周中的一天和一年中的一天——除非另有说明,否则都是从 1 开始的。对于 1900 年 3 月 1 日之前或 2099 年 12 月 31 日之后的日期,某些系统上的星期数可能是错误的。
内置函数的localtime
工作方式与gmtime
内置函数类似,但使用本地时区设置。
内置使用和输出的时间的mktime
“分解时间”表示。gmtimestrptime
内置解析与参数匹配的strptime(fmt)
输入字符串 fmt
。gmtime
输出是由 消耗和输出的“分解时间”表示mktime
。
内置使用strftime(fmt)
给定格式格式化时间(GMT)。执行strflocaltime
相同的操作,但使用本地时区设置。
strptime
和的格式字符串strftime
在典型的 C 库文档中进行了描述。ISO 8601 日期时间的格式字符串是"%Y-%m-%dT%H:%M:%SZ"
.
jq 在某些系统上可能不支持部分或全部此日期功能。特别是,macOS 不支持%u
and%j
说明符 。strptime(fmt)
SQL 风格的运算符
jq 提供了一些 SQL 风格的运算符。
- 索引(流;索引表达式):
此内置函数生成一个对象,其键由应用于给定流中每个值的给定索引表达式计算。
- 加入($idx;流;idx_expr;join_expr):
此内置函数将给定流中的值连接到给定索引。通过将给定的索引表达式应用于给定流中的每个值来计算索引的键。流中的值和索引中的相应值的数组被馈送到给定的连接表达式以产生每个结果。
- 加入($idx;流;idx_expr):
与 相同JOIN($idx; stream; idx_expr; .)
。
- 加入($idx;idx_expr):
这个内置函数将输入连接.
到给定的索引,应用给定的索引表达式.
来计算索引键。加入操作如上所述。
- 输入:
true
如果出现在给定的流中,则此内置输出.
,否则输出false
。
- IN(来源;S):
如果源流中的任何值出现在第二个流中,则此内置输出true
,否则输出false
。
builtins
返回格式为 的所有内置函数的列表name/arity
。由于具有相同名称但不同数量的函数被认为是单独的函数,因此all/0
,all/1
和all/2
都将出现在列表中。
条件和比较
==
,!=
如果 a 和 b 的结果相等(即,如果它们表示等效的 JSON 文档),则表达式 'a == b' 将产生 'true',否则将产生 'false'。特别是,字符串永远不会被视为等于数字。如果您来自 Javascript,jq 的 == 就像 Javascript 的 === - 仅当它们具有相同类型和相同值时才考虑值相等。
!= 是“不等于”,'a != b' 返回 'a == b' 的相反值
如果-那么-否则
if A then B else C end
将与 产生除 false 或 null 以外的值相同,但与B
其他情况相同。AC
if A then B end
是一样的if A then B else . end
。也就是说,else
分支是可选的,如果不存在则与.
.
检查 false 或 null 是比 Javascript 或 Python 中更简单的“真实性”概念,但这意味着您有时必须更明确地了解您想要的条件。您无法测试,例如,字符串是否为空使用if .name then A else B end
,您将需要更多类似的东西if .name then A else B end
。
如果条件A
产生多个结果,则B
对每个不为 false 或 null 的结果C
评估一次,并为每个 false 或 null 评估一次。
更多情况可以添加到 if 使用elif A then B
语法。
>, >=, <=, <
比较运算符>
, >=
, <=
,<
分别返回其左参数是否大于、大于或等于、小于或等于或小于其右参数。
顺序与上面描述的相同sort
。
和/或/不是
jq 支持普通的布尔运算符和/或/非。它们与 if 表达式具有相同的真实标准 - false 和 null 被认为是“假值”,而其他任何东西都是“真值”。
如果这些运算符之一的操作数产生多个结果,则运算符本身将为每个输入产生一个结果。
not
实际上是一个内置函数而不是一个运算符,因此它被称为过滤器,可以将事物通过管道传递给它,而不是使用特殊语法,如.foo and .bar | not
.
这三个只产生值“true”和“false”,因此只对真正的布尔运算有用,而不是常见的 Perl/Python/Ruby 习语“value_that_may_be_null or default”。如果您想使用这种形式的“或”,在两个值之间进行选择而不是评估条件,请参阅下面的“//”运算符。
替代运算符://
形式的过滤器a // b
产生与 相同的结果a
,如果a
产生 和 以外false
的结果null
。否则,a // b
产生与 相同的结果b
。
这对于提供默认值很有用:如果输入中没有元素,.foo // 1
将评估为。它类似于Python 中有时使用的方式(jq 的运算符保留用于严格的布尔运算)。1.foooror
试着抓
可以使用 捕获错误try EXP catch EXP
。执行第一个表达式,如果失败,则执行第二个表达式并显示错误消息。处理程序的输出(如果有)的输出就像它是要尝试的表达式的输出一样。
该try EXP
表单empty
用作异常处理程序。
中断控制结构
try/catch 的一个方便使用是打破控制结构,如reduce
, foreach
, while
, 等等。
例如:
代码语言:javascript复制# Repeat an expression until it raises "break" as an
# error, then stop repeating without re-raising the error.
# But if the error caught is not "break" then re-raise it.
try repeat(exp) catch .=="break" then empty else error;
jq 有一个命名词法标签的语法来“break”或“go (back) to”:
代码语言:javascript复制label $out | ... break $out ...
该break label_name表达式将使程序表现得好像最近(向左)label label_name 产生了empty.
break
和对应之间的关系label
是词法的:标签必须从中断处“可见”。
突破一个reduce
,例如:
label $out | reduce .[] as $item (null; if .==false then break $out else ... end)
以下 jq 程序产生语法错误:
代码语言:javascript复制break $out
因为没有标签$out
可见。
错误抑制/可选运算符:?
?
用作的运算符EXP?
是 的简写try EXP
。
正则表达式 (PCRE)
jq 使用 Oniguruma 正则表达式库,php、ruby、TextMate、Sublime Text 等也是如此,所以这里的描述将集中在 jq 的细节上。
定义了 jq 正则表达式过滤器,以便可以使用以下模式之一使用它们:
代码语言:javascript复制STRING | FILTER( REGEX )
STRING | FILTER( REGEX; FLAGS )
STRING | FILTER( [REGEX] )
STRING | FILTER( [REGEX, FLAGS] )
在哪里:
- STRING、REGEX 和 FLAGS 是 jq 字符串,需要进行 jq 字符串插值;
- REGEX,在字符串插值之后,应该是一个有效的 PCRE 正则表达式;
- FILTER 是、 或之一
test
,如下所述。matchcapture
FLAGS 是一个字符串,由多个受支持的标志之一组成:
g
- 全局搜索(查找所有匹配项,而不仅仅是第一个)i
- 不区分大小写的搜索m
- 多行模式('.' 将匹配换行符)n
- 忽略空匹配p
- 启用 s 和 m 模式s
- 单行模式 ('^' -> 'A', '$' -> 'Z')l
- 找到最长的匹配x
- 扩展正则表达式格式(忽略空格和注释)
要匹配 x 模式中的空格,请使用转义符,例如 s,例如
- 测试(“asb”,“x”)。
请注意,某些标志也可以在 REGEX 中指定,例如
- jq -n '(“测试”,“测试”,“测试”,“测试”)| 测试(“(?i)te(?-i)st”)'
计算结果为:真、真、假、假。
test(val)
,test(regex; flags)
Like match
,但不返回匹配对象,仅返回正则表达式true
是否false
匹配输入。
match(val)
,match(regex; flags)
match为它找到的每个匹配输出一个对象。匹配具有以下字段:
offset
- UTF-8 代码点与输入开头的偏移量length
- 匹配的 UTF-8 代码点长度string
- 它匹配的字符串captures
- 代表捕获组的对象数组。
捕获组对象具有以下字段:
offset
- UTF-8 代码点与输入开头的偏移量length
- 此捕获组的 UTF-8 代码点长度string
- 被捕获的字符串name
- 捕获组的名称(或者null
如果它未命名)
捕获不匹配任何内容的组会返回 -1 的偏移量
capture(val)
,capture(regex; flags)
在 JSON 对象中收集命名的捕获,每个捕获的名称作为键,匹配的字符串作为对应的值。
scan(regex)
,scan(regex; flags)
根据标志(如果已指定)发出与正则表达式匹配的输入的非重叠子串流。如果没有匹配,则流为空。要捕获每个输入字符串的所有匹配项,请使用成语 [ expr ]
,例如[ scan(regex) ]
.
split(regex; flags)
为了向后兼容,split
拆分字符串,而不是正则表达式。
splits(regex)
,splits(regex; flags)
它们提供与它们的对应物相同的结果split
,但作为流而不是数组。
sub(regex; tostring)
sub(regex; string; flags)
将输入字符串中正则表达式的第一个匹配项替换为tostring
, 插值后,发出字符串。 tostring
应该是一个 jq 字符串,并且可能包含对命名捕获的引用。命名的捕获实际上是作为 JSON 对象(由 构造的capture
)呈现给 的tostring
,因此对名为“x”的捕获变量的引用将采用以下形式:“(.x)”。
gsub(regex; string)
,gsub(regex; string; flags)
gsub
就像sub
但是在插值之后,正则表达式的所有非重叠出现都被字符串替换。
高级功能
变量在大多数编程语言中是绝对必要的,但它们在 jq 中被归为“高级特性”。
在大多数语言中,变量是传递数据的唯一方式。如果你计算一个值,并且你想多次使用它,你需要将它存储在一个变量中。要将值传递给程序的另一部分,您需要程序的该部分定义一个变量(作为函数参数、对象成员或其他)来放置数据。
也可以在 jq 中定义函数,尽管这是一个最大用途是定义 jq 的标准库的功能(许多 jq 函数,例如map
和find
实际上是用 jq 编写的)。
jq 有归约运算符,它们非常强大但有点棘手。同样,这些主要在内部使用,以定义 jq 标准库的一些有用位。
一开始可能并不明显,但 jq 是关于生成器的(是的,这在其他语言中很常见)。提供了一些实用程序来帮助处理生成器。
一些最小的 I/O 支持(除了从标准输入读取 JSON 并将 JSON 写入标准输出)是可用的。
最后,还有一个模块/库系统。
变量/符号绑定运算符:... as $identifier | ...
在 jq 中,所有过滤器都有一个输入和一个输出,因此不需要手动管道将值从程序的一个部分传递到下一个部分。许多表达式,例如a b
,将它们的输入传递给两个不同的子表达式(这里a
和b
都传递相同的输入),因此通常不需要使用变量来两次使用一个值。
例如,在大多数语言中,计算一个数字数组的平均值需要一些变量——至少有一个变量来保存数组,可能每个元素或循环计数器都有一个变量。在 jq 中,它很简单add / length
-add
表达式被赋予数组并产生其总和,length
表达式被赋予数组并产生其长度。
因此,通常有一种比定义变量更简洁的方法来解决 jq 中的大多数问题。尽管如此,有时它们确实使事情变得更容易,因此 jq 允许您使用expression as variable. 所有变量名都以. 这是数组平均示例的一个稍微丑陋的版本:
代码语言:javascript复制length as $array_length | add / $array_length
我们需要一个更复杂的问题来找到使用变量实际上使我们的生活更轻松的情况。
假设我们有一个博客文章数组,其中包含“作者”和“标题”字段,以及另一个用于将作者用户名映射到真实姓名的对象。我们的输入看起来像:
代码语言:javascript复制{"posts": [{"title": "Frist psot", "author": "anon"},
{"title": "A well-written article", "author": "person1"}],
"realnames": {"anon": "Anonymous Coward",
"person1": "Person McPherson"}}
我们希望生成包含真实姓名的作者字段的帖子,如下所示:
代码语言:javascript复制{"title": "Frist psot", "author": "Anonymous Coward"}
{"title": "A well-written article", "author": "Person McPherson"}
我们使用变量 $names 来存储 realnames 对象,以便稍后在查找作者用户名时引用它:
代码语言:javascript复制.realnames as $names | .posts[] | {title, author: $names[.author]}
表达式exp as x | ...意味着:对于表达式的每个值, exp使用整个原始输入运行管道的其余部分,并x设置为该值。因此as起到某种 foreach 循环的作用。
就像{foo}
方便的写作方式一样,方便的写作方式也是{foo: .foo}
如此 。{$foo}{foo:$foo}
as
通过提供与输入结构匹配的模式(这称为“解构”),可以使用单个表达式声明多个变量:
. as {realnames: $names, posts: [$first, $second]} | ...
数组模式中的变量声明(例如,. as [first, second])按顺序绑定到数组的元素,从索引零的元素开始。当数组模式元素的索引处没有值时,null将绑定到该变量。
变量的范围在定义它们的表达式的其余部分,所以
代码语言:javascript复制.realnames as $names | (.posts[] | {title, author: $names[.author]})
会工作,但是
代码语言:javascript复制(.realnames as $names | .posts[]) | {title, author: $names[.author]}
惯于。
对于编程语言理论家来说,更准确的说法是 jq 变量是词法范围的绑定。特别是没有办法改变绑定的值;只能设置一个具有相同名称的新绑定,但在旧绑定的位置不可见。
解构替代运算符:?//
解构替代运算符提供了一种简洁的机制来解构可以采用多种形式之一的输入。
假设我们有一个 API,它返回一个资源列表和与之关联的事件,我们想要获取每个资源的第一个事件的 user_id 和时间戳。如果资源有多个事件,API(已经笨拙地从 XML 转换)只会将事件包装在数组中:
代码语言:javascript复制{"resources": [{"id": 1, "kind": "widget", "events": {"action": "create", "user_id": 1, "ts": 13}},
{"id": 2, "kind": "widget", "events": [{"action": "create", "user_id": 1, "ts": 14}, {"action": "destroy", "user_id": 1, "ts": 15}]}]}
我们可以使用解构替代运算符来简单地处理这种结构变化:
代码语言:javascript复制.resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$user_id, $ts}]} | {$user_id, $kind, $id, $ts}
或者,如果我们不确定输入是值数组还是对象:
代码语言:javascript复制.[] as [$id, $kind, $user_id, $ts] ?// {$id, $kind, $user_id, $ts} | ...
每个备选方案不需要定义所有相同的变量,但所有命名变量都可用于后续表达式。在成功的替代方案中不匹配的变量将是null
:
.resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$first_user_id, $first_ts}]} | {$user_id, $first_user_id, $kind, $id, $ts, $first_ts}
此外,如果后续表达式返回错误,则替代运算符将尝试尝试下一个绑定。在最终替代过程中发生的错误将被传递。
代码语言:javascript复制[[3]] | .[] as [$a] ?// [$b] | if $a != null then error("err: ($a)") else {$a,$b} end
定义函数
您可以使用“def”语法为过滤器命名:
代码语言:javascript复制def increment: . 1;
从那时起,increment
就可以像内置函数一样用作过滤器(实际上,这就是定义了多少个内置函数)。函数可以接受参数:
def map(f): [.[] | f];
参数作为过滤器(没有参数的函数)传递,而不是作为值传递。可以使用不同的输入多次引用相同的参数(这里f
针对输入数组的每个元素运行)。函数的参数更像回调而不是值参数。理解这一点很重要。考虑:
def foo(f): f|f;
5|foo(.*2)
结果将是 20,因为f
is .*2
,并且在第一次调用f
.
将是 5,第二次将是 10 (5 * 2),所以结果将是 20。函数参数是过滤器,过滤器期望输入调用。
如果你想要定义简单函数的值参数行为,你可以只使用一个变量:
代码语言:javascript复制def addvalue(f): f as $f | map(. $f);
或者使用简写:
代码语言:javascript复制def addvalue($f): ...;
无论使用哪种定义,addvalue(.foo)
都会将当前输入的.foo
字段添加到数组的每个元素中。请注意,调用addvalue(.[])
将导致在调用站点map(. $f)
的值中的每个值对部件进行一次评估。.
允许使用相同函数名的多个定义。对于相同数量的函数参数,每个重新定义都会替换先前的重新定义,但仅适用于重新定义之后的函数(或主程序)的引用。另请参阅下面有关范围界定的部分。
范围界定
jq 中有两种类型的符号:值绑定(又名“变量”)和函数。两者都是词法范围的,表达式只能引用已在它们“左侧”定义的符号。该规则的唯一例外是函数可以引用自身以便能够创建递归函数。
例如,在下面的表达式中,有一个绑定在它的“右侧”可见... | .*3 as times_three | [. times_three] | ...,但在“左侧”不可见。现在考虑这个表达式... | (.*3 as times_three | [. times_three]) | ...:这里的绑定 在右括号之后
减少
jq 中的reduce
语法允许您通过将表达式的所有结果累积成一个答案来组合它们。例如,我们将传递[3,2,1]
给这个表达式:
reduce .[] as $item (0; . $item)
对于.[]
产生的每个结果,. $item
运行以累积从 0 开始的运行总计。在此示例中,.[]
产生结果 3、2 和 1,因此效果类似于运行以下内容:
0 | (3 as $item | . $item) |
(2 as $item | . $item) |
(1 as $item | . $item)
isempty(exp)
exp
如果不产生输出,则返回 true ,否则返回 false。
limit(n; exp)
该limit
函数最多从 中提取n
输出exp
。
first(expr)
, last(expr)
,nth(n; expr)
first(expr)
和函数分别从中last(expr)
提取第一个和最后一个值expr
。
该nth(n; expr)
函数提取由 输出的第 n 个值 expr
。这可以定义为def nth(n; expr): last(limit(n 1; expr));
。请注意,nth(n; expr)
不支持n
.
first
, last
,nth(n)
first
and函数从位于的last
任何数组中提取第一个和最后一个值.
。
该nth(n)
函数在 处提取任何数组的第 n 个值.
。
foreach
foreach
语法类似于reduce
,但旨在允许构造limit
和生成中间结果的化简器(参见示例)。
形式是foreach EXP as var (INIT; UPDATE; EXTRACT)。像reduce,INIT被评估一次以产生一个状态值,然后每个输出EXP绑定到var,UPDATE 被评估为EXP具有当前状态和
这仅对构造reduce
- 和 - limit
类似的函数非常有用。但它更通用,因为它允许部分减少(参见下面的示例)。
递归
如上所述,recurse
使用递归,任何jq函数都可以递归。while
内置函数也以递归的方式实现。
只要递归调用左侧的表达式输出其最后一个值,就会优化尾调用。实际上,这意味着递归调用左侧的表达式不应为每个输入生成多个输出。
例如:
代码语言:javascript复制def recurse(f): def r: ., (f | select(. != null) | r); r;
def while(cond; update):
def _while:
if cond then ., (update | _while) else empty end;
_while;
def repeat(exp):
def _repeat:
exp, _repeat;
_repeat;
生成器和迭代器
一些 jq 运算符和函数实际上是生成器,因为它们可以为每个输入生成零、一个或多个值,正如人们在其他具有生成器的编程语言中所期望的那样。例如,.[]
生成其输入中的所有值(必须是数组或对象),range(0; 10)
生成 0 到 10 之间的整数,等等。
甚至逗号运算符也是一个生成器,它首先生成由逗号左侧的表达式生成的值,然后对于其中的每一个,生成由逗号右侧的表达式生成的值。
empty
内置是产生零输出的生成器。empty
内置回溯到前面的生成器表达式。
All jq functions can be generators just by using builtin generators. It is also possible to define new generators using only recursion and the comma operator. If the recursive call(s) is(are) "in tail position" then the generator will be efficient. In the example below the recursive call by _range
to itself is in tail position. The example shows off three advanced topics: tail recursion, generator construction, and sub-functions.
Examples
Math
jq currently only has IEEE754 double-precision (64-bit) floating point number support.
Besides simple arithmetic operators such as
, jq also has most standard math functions from the C math library. C math functions that take a single input argument (e.g., sin()
) are available as zero-argument jq functions. C math functions that take two input arguments (e.g., pow()
) are available as two-argument jq functions that ignore .
. C math functions that take three input arguments are available as three-argument jq functions that ignore .
.
Availability of standard math functions depends on the availability of the corresponding math functions in your operating system and C math library. Unavailable math functions will be defined but will raise an error.
One-input C math functions: acos
acosh
asin
asinh
atan
atanh
cbrt
ceil
cos
cosh
erf
erfc
exp
exp10
exp2
expm1
fabs
floor
gamma
j0
j1
lgamma
log
log10
log1p
log2
logb
nearbyint
pow10
rint
round
significand
sin
sinh
sqrt
tan
tanh
tgamma
trunc
y0
y1
.
Two-input C math functions: atan2
copysign
drem
fdim
fmax
fmin
fmod
frexp
hypot
jn
ldexp
modf
nextafter
nexttoward
pow
remainder
scalb
scalbln
yn
.
Three-input C math functions: fma
.
See your system's manual for more information on each of these.
I/O
At this time jq has minimal support for I/O, mostly in the form of control over when inputs are read. Two builtins functions are provided for this, input
and inputs
, that read from the same sources (e.g., stdin
, files named on the command-line) as jq itself. These two builtins, and jq's own reading actions, can be interleaved with each other.
两个内置函数提供最小的输出功能debug
, 和 stderr
. (回想一下,jq 程序的输出值总是作为 JSON 文本输出到stdout
.)debug
内置可以具有特定于应用程序的行为,例如对于使用 libjq C API 但不是 jq 可执行文件本身的可执行文件。内置将stderr
其输入以原始模式输出到 stder,没有额外的装饰,甚至没有换行符。
大多数 jq 内置函数在引用上是透明的,并且在应用于常量输入时会产生常量和可重复的值流。这不适用于 I/O 内置函数。
input
输出一个新的输入。
inputs
一个一个地输出所有剩余的输入。
这主要用于减少程序的输入。
debug
产生基于输入值的调试消息。jq 可执行文件用输入值包装 ["DEBUG:", <input-value>]
并在 stderr 上紧凑地打印该值和换行符。这在未来可能会改变。
stderr
以原始和紧凑模式将其输入打印到 stderr,没有额外的装饰,甚至没有换行符。
input_filename
返回当前正在过滤其输入的文件的名称。请注意,除非 jq 在 UTF-8 语言环境中运行,否则这将无法正常工作。
input_line_number
返回当前被过滤的输入的行号。
流式操作
使用--stream
选项 jq 可以以流方式解析输入文本,允许 jq 程序立即开始处理大型 JSON 文本,而不是在解析完成后。如果您有一个大小为 1GB 的 JSON 文本,流式传输将使您能够更快地处理它。
然而,流处理并不容易,因为 jq 程序将[<path>, <leaf-value>]
(和一些其他形式)作为输入。
提供了几个内置函数以使处理流更容易。
下面的示例使用 的流式形式[0,[1]]
,即 [[0],0],[[1,0],1],[[1,0]],[[1]]
.
流形式包括[<path>, <leaf-value>]
(表示任何标量值、空数组或空对象)和[<path>]
(表示数组或对象的结尾)。未来版本的 jq 运行--stream
并-seq
可能输出其他形式,例如 ["error message"]
当输入文本无法解析时。
truncate_stream(stream_expression)
使用一个数字作为输入,并从给定流表达式的输出左侧截断相应数量的路径元素。
fromstream(stream_expression)
输出与流表达式的输出对应的值。
tostream
tostream
内置输出其输入的流式形式。
任务
jq 中的赋值工作与大多数编程语言中的工作方式略有不同。jq 不区分对某事物的引用和副本——两个对象或数组相等或不相等,没有任何进一步的“相同对象”或“不同对象”的概念。
如果一个对象有两个字段,它们是数组.foo
和.bar
,并且你在 上附加了一些东西.foo
,那么.bar
即使你之前设置了 ,也不会变大.bar = .foo
。如果您习惯于使用 Python、Java、Ruby、Javascript 等语言进行编程,那么您可以将其想象为 jq 在执行分配之前对每个对象进行了完整的深度复制(出于性能考虑,它实际上并没有这样做,但这是一般的想法)。
这意味着不可能在 jq 中构建循环值(例如第一个元素是自身的数组)。这是非常有意的,并确保 jq 程序可以生成的任何内容都可以用 JSON 表示。
jq 中的所有赋值运算符在左侧(LHS)都有路径表达式。右侧 (RHS) 提供设置为由 LHS 路径表达式命名的路径的值。
jq 中的值始终是不可变的。在内部,赋值通过使用归约来计算新的替换值,.
所有期望的赋值都应用于.
,然后输出修改后的值。这个可以说明这一点:{a:{b:{c:1}}} | (.a.b|=3), .
. 这将输出 {"a":{"b":3}}
,{"a":{"b":{"c":1}}}
因为最后一个子表达式.
, 看到的是原始值,而不是修改后的值。
大多数用户都希望使用修改赋值运算符,例如|=
or =
,而不是=
。
请注意,赋值运算符的 LHS 是指 .. 因此var.foo = 1不会按预期工作(var.foo在 中不是有效或有用的路径表达式.);改为使用
还要注意,.a,.b=0
不设置.a
and .b
,而是 (.a,.b)=0
设置两者。
更新分配:|=
这是“更新”运算符'|='。它在右侧采用一个过滤器,并.
通过该表达式运行旧值来计算分配给的属性的新值。例如, (.foo, .bar) |= . 1 将构建一个对象,其中“foo”字段设置为输入的“foo”加 1,“bar”字段设置为输入的“bar”加 1 .
左侧可以是任何通用路径表达式;见path()
。
请注意,'|=' 的左侧指的是.. 因此var.foo |= . 1不会按预期工作(var.foo在 中不是有效或有用的路径表达式.);改为使用
如果右侧不输出任何值(即empty
),则左侧路径将被删除,与 一样del(path)
。
如果右侧输出多个值,则仅使用第一个值(兼容性说明:在 jq 1.5 及更早版本中,过去是仅使用最后一个)。
算术更新赋值: =
, -=
, *=
, /=
, %=
,//=
jq 有几个形式的运算符a op= b
,它们都等价于a |= . op b
. 因此, = 1
可用于增加值,与|= . 1
.
简单的分配:=
这是普通的赋值运算符。与其他不同的是,右侧 (RHS) 的输入与左侧 (LHS) 的输入相同,而不是 LHS 路径上的值,并且 RHS 输出的所有值都将是使用(如下图)。
如果 '=' 的 RHS 产生多个值,那么对于每个这样的值 jq 将左侧的路径设置为该值,然后它将输出修改后的.
. 例如, (.a,.b)=range(2)
输出{"a":0,"b":0}
,然后 {"a":1,"b":1}
。“更新”分配表格(见上文)不这样做。
这个应该显示 '=' 和 '|=' 之间的区别:
向程序提供输入 '{"a": {"b": 10}, "b": 20}':
.a = .b
.a |= .b
前者将输入的“a”字段设置为输入的“b”字段,并产生输出{“a”:20,“b”:20}。后者会将输入的“a”字段设置为“a”字段的“b”字段,产生{“a”:10,“b”:20}。
'=' 和 '|=' 之间区别的另一个:
空|(.a,.b)=范围(3)
输出 '{"a":0,"b":0}'、'{"a":1,"b":1}' 和 '{"a":2,"b":2}',尽管
空|(.a,.b)|=范围(3)
只输出'{"a":0,"b":0}'。
复杂的任务
与大多数语言相比,jq 赋值左侧允许的内容更多。我们已经在左侧看到了简单的字段访问,数组访问也同样有效也就不足为奇了:
代码语言:javascript复制.posts[0].title = "JQ Manual"
令人惊讶的是,左边的表达式可能会产生多个结果,指的是输入文档中的不同点:
代码语言:javascript复制.posts[].comments |= . ["this is great"]
该示例将字符串“this is great”附加到输入中每个帖子的“comments”数组(其中输入是具有字段“posts”的对象,该字段是帖子数组)。
当 jq 遇到像 'a = b' 这样的赋值时,它会记录在执行 a 时选择输入文档的一部分所采用的“路径”。然后,此路径用于查找在执行分配时要更改输入的哪一部分。任何过滤器都可以用在等号的左侧——它从输入中选择的任何路径都将是执行分配的地方。
这是一个非常强大的操作。假设我们想为博客文章添加评论,使用上面相同的“博客”输入。这一次,我们只想评论“stedolan”写的帖子。我们可以使用前面描述的“选择”功能找到这些帖子:
代码语言:javascript复制.posts[] | select(.author == "stedolan")
该操作提供的路径指向“stedolan”写的每一个帖子,我们可以像之前一样对每一个帖子进行评论:
代码语言:javascript复制(.posts[] | select(.author == "stedolan") | .comments) |=
. ["terrible."]
模块
jq 有一个库/模块系统。模块是名称以 . 结尾的文件.jq
。
程序导入的模块在默认搜索路径中搜索(见下文)。和指令允许导入import
器include
更改此路径。
搜索路径中的路径会受到各种替换。
对于以“~/”开头的路径,用户的主目录将替换为“~”。
对于以“ORIGIN/”开头的路径,jq 可执行文件的路径将替换为“ORIGIN”。
对于以“./”开头的路径或以“.”开头的路径,包含文件的路径将替换为“.”。对于命令行上给出的顶级程序,使用当前目录。
导入指令可以选择指定附加默认值的搜索路径。
默认搜索路径是赋予-L 命令行选项 else的搜索路径["~/.jq", "
Null 和空字符串路径元素终止搜索路径处理。
将在给定搜索路径的“foo/bar.jq”和“foo/bar/bar.jq”中搜索具有相对路径“foo/bar”的依赖项。这旨在允许将模块与例如版本控制文件、自述文件等一起放置在目录中,但也允许单文件模块。
不允许具有相同名称的连续组件以避免歧义(例如,“foo/foo”)。
例如,可以在 和中找到-L$HOME/.jq
一个模块。foo$HOME/.jq/foo.jq$HOME/.jq/foo/foo.jq
如果“$HOME/.jq”是一个文件,它会被引入主程序。
import RelativePathString as NAME [<metadata>];
导入在相对于搜索路径中的目录的给定路径中找到的模块。“.jq”后缀将添加到相对路径字符串中。模块的符号以“NAME::”为前缀。
可选元数据必须是常量 jq 表达式。它应该是一个带有“主页”等键的对象。此时 jq 只使用元数据的“搜索”键/值。元数据也通过 modulemeta
内置提供给用户。
元数据中的“搜索”键(如果存在)应具有字符串或数组值(字符串数组);这是作为顶级搜索路径前缀的搜索路径。
include RelativePathString [<metadata>];
导入在给定路径中找到的模块,该模块相对于搜索路径中的目录,就好像它被包含在适当位置一样。“.jq”后缀将添加到相对路径字符串中。模块的符号被导入调用者的命名空间,就好像模块的内容被直接包含在内一样。
可选元数据必须是常量 jq 表达式。它应该是一个带有“主页”等键的对象。此时 jq 只使用元数据的“搜索”键/值。元数据也通过 modulemeta
内置提供给用户。
import RelativePathString as $NAME [<metadata>];
导入在相对于搜索路径中的目录的给定路径中找到的 JSON 文件。“.json”后缀将添加到相对路径字符串中。该文件的数据将以$NAME::NAME
.
可选元数据必须是常量 jq 表达式。它应该是一个带有“主页”等键的对象。此时 jq 只使用元数据的“搜索”键/值。元数据也通过 modulemeta
内置提供给用户。
元数据中的“搜索”键(如果存在)应具有字符串或数组值(字符串数组);这是作为顶级搜索路径前缀的搜索路径。
module <metadata>;
该指令完全是可选的。它不是正确操作所必需的。它仅用于提供可以使用modulemeta
内置函数读取的元数据。
元数据必须是常量 jq 表达式。它应该是一个带有“主页”之类的键的对象。此时 jq 不使用此元数据,但它通过 modulemeta
内置提供给用户。
modulemeta
将模块名称作为输入并将模块的元数据作为对象输出,模块的导入(包括元数据)作为“deps”键的数组值。
程序可以使用它来查询模块的元数据,然后他们可以使用它来搜索、下载和安装缺少的依赖项。
颜色
要配置替代颜色,只需将JQ_COLORS
环境变量设置为以冒号分隔的部分终端转义序列列表,如"1;31"
,按以下顺序:
- 颜色为
null
- 颜色为
false
- 颜色为
true
- 数字的颜色
- 字符串的颜色
- 数组的颜色
- 物体的颜色
默认配色方案与设置相同 "JQ_COLORS=1;30:0;37:0;37:0;37:0;32:1;37:1;37"
。
这不是 VT100/ANSI 转义的手册。但是,这些颜色规范中的每一个都应包含两个用分号分隔的数字,其中第一个数字是以下数字之一:
- 1(亮)
- 2(暗淡)
- 4(下划线)
- 5(闪烁)
- 7(反向)
- 8(隐藏)
第二个是其中之一:
- 30(黑色)
- 31(红色)
- 32(绿色)
- 33(黄色)
- 34(蓝色)
- 35(洋红色)
- 36(青色)
- 37(白色)
高级用法示例
打印json中around属性中的OtherCars属性中的数组中第12个元素大于0的数组,其中点号表示当前节点即around.OtherCars[]:
汇总:
jq可以将JSON来切片、过滤、映射和转换结构化数据,就像 ,sed
和awkgrep
让您玩文本一样容易。
jq很灵活,再搭配着其他bash命令那就不要太强大啦~
参考:
https://github.com/stedolan/jq