jq工具的使用
jq 命令行行为的完整规则
| 命令格式 | 行为 | 示例 | 结果 |
|---|---|---|---|
jq (无任何参数) |
显示帮助信息 | jq |
显示帮助文档 |
jq <选项> (无过滤器) |
显示帮助信息 | jq -cjq -n |
显示帮助文档 |
jq <过滤器> |
阻塞等待输入 | jq '.'jq 'map(.id)' |
等待 STDIN |
jq <选项> <过滤器> |
取决于选项: - 普通选项:等待输入 - -n:不等待 |
jq -c '.' → 等待jq -n '.' → 立即执行 |
|
jq <过滤器> 文件 |
立即处理文件 | jq '.' data.json |
处理文件内容 |
jq <选项> <过滤器> 文件 |
立即处理文件 | jq -c '.' data.json |
处理文件内容 |
1. 只有选项 → 显示帮助
$ jq -c
jq - commandline JSON processor [version 1.6]
Usage: jq [options] <jq filter> [file...]
...
# 立即显示帮助文档,不阻塞
2. 只有过滤器 → 阻塞等待
$ jq '.name'
(光标闪烁,程序挂起等待输入)
# 需要按 Ctrl+D 结束输入或 Ctrl+C 终止
3. 过滤器 + -n → 立即执行
$ jq -n '.name'
null # 立即输出结果(因为没有输入)
4. 过滤器 + 文件 → 立即处理
$ jq '.name' data.json
"Alice" # 立即从文件处理
为什么这个区别很重要?
在实际脚本编写中:
#!/bin/bash
# 危险:会阻塞脚本执行!
result=$(jq '.data')
# 安全:立即生成数据
result=$(jq -n '{time: now}')
# 安全:从变量读取
json='{"value": 42}'
result=$(jq '.value' <<< "$json")
基本操作:
jq [options] <jq filter> [file...]
[options] - 可选选项
-
-r:原始输出模式 -
-c:紧凑输出 -
-n:无输入模式
<jq filter> - 必填过滤器
:用单引号包裹的表达式 '...'
-
'.':恒等过滤器 -
'.name':字段选择器 -
'map(.id)':数组处理
[file...] - 可选输入文件
-
格式:文件路径(一个或多个)
-
-
data.json:单个文件 -
file1.json file2.json:多个文件
-
输入jq .后阻塞,然后我键盘输入a,报parse error: invalid numeric literal at line2,column 0,为啥报这个错误
-
jq 进入等待输入状态(因为没有使用
-n选项且没有提供文件) -
光标在终端闪烁,等待您输入 JSON 数据
jq 要求输入必须是有效的 JSON,而 a 不是合法的 JSON。有效的 JSON 值必须是:
-
字符串:
"a"(必须用双引号包裹) -
数字:
123 -
布尔值:
true或false -
对象:
{"key": "value"} -
数组:
[1, 2, 3] -
null:
null
虽然您输入的是字母,但 jq 的解析器是这样工作的:
-
首先尝试解析为数字(因为数字是 JSON 的基本类型)
-
当看到
a时,它可能期待类似0xA的十六进制数字 -
发现后面没有有效数字字符时抛出错误
1. JSON 格式要求 jq 要求输入必须是有效的 JSON,而 a 不是合法的 JSON。有效的 JSON 值必须是: 字符串:"a"(必须用双引号包裹) 数字:123 布尔值:true 或 false 对象:{"key": "value"} 数组:[1, 2, 3] null:null 不对吧,json不是key,value么,只输入上面的值也不是json啊?
真正的JSON格式要求:
-
顶层必须是以下之一:
-
对象(用
{}包裹) -
数组(用
[]包裹)
-
-
值类型:
-
字符串:必须双引号包裹
"text" -
数字:整数或浮点数
42,3.14 -
布尔值:
true或false -
空值:
null -
对象:
{"key": "value"} -
数组:
[1, 2, 3]
-
然JSON标准要求顶层必须是对象或数组,但jq对输入的处理更灵活:
-
jq接受任何有效的JSON值作为输入,包括:
-
简单值:
"string",42,true,null -
复合值:
{...},[...]
-
-
这是jq的特性,不是JSON标准的特性:
# 这些在jq中都是有效的输入 echo '"text"' | jq . # 字符串 echo '42' | jq . # 数字 echo 'true' | jq . # 布尔值
为什么jq允许简单值?
这是jq设计者的选择:
-
实用主义:很多场景只需要处理简单值
-
管道友好:便于在命令行中处理各种数据
-
符合脚本需求:例如
jq -n '1 + 1'直接计算
标准JSON vs jq输入对比
| 类型 | 标准JSON是否有效 | jq是否接受作为输入 |
|---|---|---|
{"key": "value"} |
✅ | ✅ |
["a", "b"] |
✅ | ✅ |
"string" |
❌(顶层无效) | ✅ |
42 |
❌(顶层无效) | ✅ |
true |
❌(顶层无效) | ✅ |
null |
❌(顶层无效) | ✅ |
a |
❌ | ❌ |
json的key必须是字符串,value是上面几种数据类型?
JSON 格式的完整规范
-
顶层结构:
-
必须是对象(
{})或数组([]) -
不能是简单值(字符串、数字、布尔值或 null)
-
-
键(Key)要求:
-
必须是字符串
-
必须用双引号包裹
-
不能是数字、布尔值或其他类型
// 正确 {"name": "Alice"} // 错误 - 键未加引号 {name: "Alice"} // 错误 - 键是数字 {42: "answer"} -
-
值(Value)类型:
类型 示例 说明 字符串 "text"必须双引号包裹 数字 42,3.14整数或浮点数 布尔值 true,false必须小写 数组 [1, "two", true]有序值集合 对象 {"key": "value"}键值对集合 null null表示空值
虽然标准 JSON 要求顶层必须是对象或数组,但 jq 放宽了这个限制:
-
jq 接受简单值作为输入:
# 这些在 jq 中有效但不符合 JSON 标准 echo '"text"' | jq . # 字符串 echo '42' | jq . # 数字 echo 'true' | jq . # 布尔值 -
但键仍然必须遵循 JSON 规范:
# 尝试使用无效键 echo '{name: "Alice"}' | jq . # 报错:parse error: Invalid numeric literal at line 1 # 正确写法 echo '{"name": "Alice"}' | jq .
# 查看特定主机的 facts
ansible 172.17.79.16 -i hosts -m setup -o 2>/dev/null | awk-F '=> ''{print $2}'| jq .
或者ansible 172.17.79.16 -i hosts -m setup -o 2>/dev/null | tr -d "172.17.79.16 | success => "| less
-
对于数组:使用索引访问
.[0],.[1] -
对于对象:使用键名访问
.["key"]或.key -
keys函数适用于两种结构:-
数组 → 返回索引
-
对象 → 返回键名
-
-
组合使用管道
|可以构建复杂查询
这两种访问方式是 jq 处理 JSON 的核心功能,掌握它们就能高效提取和转换 JSON 数据
the simplest filter is .,which is the identity filter
在jq工具中,.(点号)是最简单的过滤器,称为"identity filter"(恒等过滤器)。它的作用是将输入的JSON数据原样输出,仅对格式进行标准化处理(如美化缩进)
identity(恒等)这一术语源自数学和计算机科学中的概念,指代一种不改变输入数据的操作,词根"ident"源于拉丁语"idem"(相同)48,后缀"-ity"表示性质或状态,组合后表示"同一性"或"不变性",如同身份证(identity card)唯一对应个人身份,区别于动词"identify"(识别)的动态过程,此处强调静态的"同一性"
例1:
jq '.[0] | keys' array_of_objects.json
array_of_objects.json 内容:
[
{"id": 1, "name": "Alice", "active": true},
{"id": 2, "email": "bob@example.com"},
{"role": "admin", "permissions": ["read", "write"]}
]
-
.[0]获取数组的第一个元素{"id":1,"name":"Alice","active":true} -
keys提取这个对象的所有键名(按字母排序)
例2:.[]在数组和字典(对象)中的展开不同
对象的展开:
a.json的内容如下:
{ "id": 1, "name": "Alice", "roles": ["admin", "user"] }
命令jq '.[] | keys' a.json:
'.[]' a.json 展开的字典的value
结果:
1
Alice
["admin", "user"]
{"user": {"name": "Alice", "age": 30}, "server": {"ip": "1.1.1.1"}}
jq '.[] | keys' file.json
输出:
["age","name"]
["ip"]
数组的展开:
假设你的file.json:
[
{
"id": 1,
"name": "Alice",
"roles": ["admin", "user"]
},
{
"id": 2,
"name": "Bob",
"email": "bob@example.com"
},
{
"company": "Tech Inc",
"country": "USA"
}
]
-
.[]:展开数组中的所有元素{"id":1,"name":"Alice","roles":["admin","user"]} {"id":2,"name":"Bob","email":"bob@example.com"} {"company":"Tech Inc","country":"USA"} -
| keys:对每个展开的对象获取键名["id","name","roles"] ["id","name","email"] ["company","country"]
最终输出:
[
"id",
"name",
"roles"
]
[
"id",
"name",
"email"
]
[
"company",
"country"
]
.[] - 数组展开运算符
-
作用:将数组拆分为独立的元素
-
输入:
[元素1, 元素2, ...] -
输出:
元素1元素2...(多个独立 JSON 对象) -
如果输入是对象:
{"a":1, "b":2}→ 输出值12
jq '.object.nested_array[].property' file.json
.object # 第一层:访问根对象的 "object" 属性
.nested_array # 第二层:访问 object 对象的 "nested_array" 属性
[] # 解包操作:将 nested_array 数组展开为多个元素
.property # 第三层:访问每个数组元素的 "property" 属性
假设你的 file.json 内容
{
"object": {
"nested_array": [
{
"id": 1,
"property": "value1",
"details": {"sub": "a"}
},
{
"id": 2,
"property": "value2",
"details": {"sub": "b"}
}
],
"other_field": "ignored"
},
"unrelated": "data"
}
运行命令:
jq '.object.nested_array[].property' file.json
输出:
"value1"
"value2"
关键概念解释
-
.(根选择器)-
起始点,表示整个 JSON 文档的根
-
类似文件系统的
/根目录
-
-
.object(属性访问)-
访问根对象的
object属性 -
等价于 JavaScript 的
data.object
-
-
.nested_array(嵌套访问)-
访问
object对象的nested_array属性 -
类似
data.object.nested_array
-
-
[](数组迭代器)-
最关键的操作符:将数组展开为独立的元素
-
输入:
[ {...}, {...} ]
输出:{...}和{...}(两个独立对象)
-
-
.property(叶节点访问)-
从每个展开的数组元素中提取
property值 -
对每个元素执行:
element.property
-
类比其他语言
| jq 表达式 | JavaScript 等价 | Python 等价 |
|---|---|---|
.object |
data.object |
data['object'] |
.nested_array[] |
data.object.nested_array.forEach(...) |
for item in data['object']['nested_array']: |
.property |
item.property |
item['property'] |
# 查看特定主机的 facts
ansible 172.17.79.16 -i hosts -m setup -o 2>/dev/null | awk-F '=> ''{print $2}'| jq .
或者ansible 172.17.79.16 -i hosts -m setup -o 2>/dev/null | tr -d "172.17.79.16 | success => "| less
如果ansible输出的不是单行(比如没有使用`-o`选项),那么上述命令可能会失败,因为ansible默认输出的JSON是带缩进的多行格式,
而awk是按行处理的,这样每行都会被分割,导致输出多行内容给jq,而jq期望输入是一个完整的JSON文档。
-
ansible ... -o-o参数使输出为单行(关键!)172.17.79.16 | SUCCESS => {JSON数据} -
awk -F'=> ' '{print $2}'
以"=> "为分隔符,提取第二部分(纯JSON) -
jq .
美化输出 JSON
shell中的数组arr=(a b c d)
echo ${a[0]}
和
declare -A arr1=([0]=a [1]=b [2]=c [3]=d)
echo ${b[0]}
结果一样,有啥区别,难道普通数组可以看作关联数组的特殊数组?
是的,索引数组可以看作关联数组的特殊形式(键为连续整数),但在 Shell 实现中它们是不同的数据结构:
-
索引数组:为顺序访问优化的数组
-
关联数组:通用的键值存储
在您的例子中,当使用连续整数作为关联数组的键时,行为确实类似于索引数组,但底层实现和特性仍有差异。
因X.Y和X["Y"]都行,所以
ansible 172.17.69.16 -i hosts -m setup --key-file=./ssh/id_rsa -o 2>/dev/null | \
awk -F "=> " '{print $2}'|jq '.ansible_facts.ansible_nodename'
等价于
ansible 172.17.69.16 -i hosts -m setup --key-file=./ssh/id_rsa -o 2>/dev/null | \
awk -F "=> " '{print $2}'|jq '.["ansible_facts"]["ansible_nodename"]'
.是文本对象
查看ansible_facts所有key
ansible 172.17.69.16 -i hosts -m setup --key-file=./ssh/id_rsa -o 2>/dev/null | \
awk -F "=> " '{print $2}'|jq '.[]'
ansible 172.17.69.16 -i hosts -m setup --key-file=./ssh/id_rsa -o 2>/dev/null | \
awk -F "=> " '{print $2}'|jq '.ansible_facts|keys'

浙公网安备 33010602011771号