AI Infra 训练营
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub

jq —— JSON 命令行处理器

一句话定义

jq 是给 JSON 用的"sed/awk"——读 JSON,按你给的表达式提字段 / 过滤 / 变形,输出新的 JSON 或纯文本。几乎所有现代 API 输出都是 JSON,K8s(kubectl ... -o json)、curl 调用 REST API、journalctl -o json ... 没有 jq 寸步难行。

典型场景

# kubectl 输出二次加工
kubectl get pods -o json | jq '.items[].metadata.name'

# 提取某个值
curl -s api.github.com/users/torvalds | jq '.public_repos'

# 看结构
echo '{"a":{"b":[1,2,3]}}' | jq

# 过滤数组
kubectl get pods -o json | jq '.items[] | select(.status.phase != "Running")'

安装

apt-get install -y jq            # Ubuntu / Debian
yum install -y jq                # CentOS / RHEL
brew install jq                  # macOS

K8s 节点训练营装机脚本里直接装上。


第 1 步:理解 jq 表达式的基础语法

jq 表达式 = 过滤器(filter)。给 jq 一个 JSON 输入,过滤器算出一个或多个输出。

最简单的过滤器:

echo '{"name":"k8s","version":1.28}' | jq '.'         # 整体(pretty print)
echo '{"name":"k8s","version":1.28}' | jq '.name'     # 提字段:k8s
echo '{"name":"k8s","version":1.28}' | jq '.version'  # 1.28

. 表示当前输入。.name 表示输入对象的 name 字段。


字段访问

echo '{"a":{"b":{"c":42}}}' | jq '.a.b.c'             # 42

# 数组下标
echo '[10,20,30]' | jq '.[0]'                         # 10
echo '[10,20,30]' | jq '.[-1]'                        # 30(倒数)
echo '[10,20,30]' | jq '.[0:2]'                       # [10,20](切片)

# 字段 + 数组混合
echo '{"items":[{"name":"a"},{"name":"b"}]}' | jq '.items[0].name'    # "a"

.[] 展开数组

echo '[10,20,30]' | jq '.[]'
# 10
# 20
# 30

注意:.[] 输出多个值(流式),不是一个数组。后续操作对每个值都跑一遍。

echo '[{"name":"a"},{"name":"b"}]' | jq '.[].name'
# "a"
# "b"

字段可能不存在:?

echo '[{"name":"a"},{"id":1}]' | jq '.[].name'           # 报错:can't index 1
echo '[{"name":"a"},{"id":1}]' | jq '.[].name?'          # ✅ ? 容错,缺失就不输出

? 让 jq 在字段访问错误时返回空而不是报错。


管道 |(jq 内部的)

jq 的管道和 shell 管道很像,但是 jq 内部的:

echo '{"a":[1,2,3]}' | jq '.a | .[0]'                 # 1
echo '{"a":[1,2,3]}' | jq '.a[0]'                     # 1(等价)

echo '[{"n":"a"},{"n":"b"}]' | jq '.[] | .n'          # "a" "b"
echo '[{"n":"a"},{"n":"b"}]' | jq '.[].n'             # 等价

| 把左边的输出喂给右边继续处理。


选择字段并组成新对象

echo '{"name":"k8s","ver":1.28,"size":42}' | jq '{name, ver}'
# {"name":"k8s","ver":1.28}

echo '{"a":1,"b":2}' | jq '{x: .a, y: .b}'
# {"x":1,"y":2}

K8s 实际场景:

kubectl get pods -o json | jq '.items[] | {name: .metadata.name, status: .status.phase}'

select(...) 按条件过滤

echo '[1,2,3,4,5]' | jq '.[] | select(. > 2)'
# 3 4 5

kubectl get pods -o json \
  | jq '.items[] | select(.status.phase != "Running") | .metadata.name'
# 列出所有非 Running 的 pod 名

kubectl get pods -A -o json \
  | jq '.items[] | select(.metadata.namespace == "kube-system") | .metadata.name'
# kube-system 命名空间下的 pod

select(condition) —— 满足条件的值通过,否则丢弃。


-r 输出纯字符串(生产用得最多)

echo '{"name":"k8s"}' | jq '.name'
# "k8s"                                    ← 带引号

echo '{"name":"k8s"}' | jq -r '.name'
# k8s                                      ← 不带引号

-r (raw) 去掉 JSON 字符串的引号。把 jq 输出再交给 shell / 别的工具时几乎一定要 -r:

# 拿所有 pod 名做下一步
PODS=$(kubectl get pods -o json | jq -r '.items[].metadata.name')
for p in $PODS; do kubectl logs "$p"; done

没 -r 的话 $p 会带着引号 "podname",传给 kubectl 就出错了。


常用内置函数

函数用途
keys取对象的所有 key(数组)
values取对象所有 value
length长度(数组 / 字符串 / 对象 key 数)
type类型("object", "array", "string", "number", "boolean", "null")
has("key")是否含某 key
contains(value)是否包含某值
map(expr)对数组每个元素跑 expr
select(cond)过滤
tostring / tonumber类型转换
split("sep") / join("sep")字符串拆 / 拼
unique / sort / reverse数组操作
group_by(expr)按表达式分组(返回二维数组)
min / max / add聚合
to_entries / from_entries对象 ↔ key-value 数组
echo '[3,1,4,1,5,9,2,6]' | jq 'sort'                     # [1,1,2,3,4,5,6,9]
echo '[3,1,4,1,5,9,2,6]' | jq 'sort | reverse'           # 倒序
echo '[3,1,4,1,5,9,2,6]' | jq 'unique'                   # [1,2,3,4,5,6,9]
echo '[3,1,4,1,5,9,2,6]' | jq 'add'                      # 31(求和)
echo '[3,1,4,1,5,9,2,6]' | jq 'length'                   # 8
echo '{"a":1,"b":2}' | jq 'keys'                         # ["a","b"]

map() 对数组每个元素操作

echo '[1,2,3]' | jq 'map(. + 10)'           # [11, 12, 13]
echo '[1,2,3]' | jq 'map(. * .)'            # [1, 4, 9]

kubectl get pods -o json \
  | jq '.items | map(.metadata.name)'        # 名字数组

map(expr) ≈ [.[] | expr] —— 对每个元素跑 expr、收集成数组。


字符串格式化

echo '{"a":1,"b":2}' | jq '"\(.a)/\(.b)"'   # "1/2"
echo '{"a":1,"b":2}' | jq -r '"\(.a)/\(.b)"'   # 1/2

# K8s pod 名 + 状态拼接
kubectl get pods -o json \
  | jq -r '.items[] | "\(.metadata.name)\t\(.status.phase)"'

\(expr) 是字符串插值(jq 里的语法,不是 shell)。


训练营常用 jq 套路

套路 1:所有 pod 名 + namespace(表格化)

kubectl get pods -A -o json \
  | jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name)"' \
  | column -t -s/

套路 2:找非 Running 的 pod

kubectl get pods -A -o json \
  | jq -r '.items[] | select(.status.phase != "Running") 
           | "\(.metadata.namespace)/\(.metadata.name) \(.status.phase)"'

套路 3:节点 IP 清单

kubectl get nodes -o json \
  | jq -r '.items[].status.addresses[] | select(.type=="InternalIP") | .address'

套路 4:容器镜像清单

kubectl get pods -A -o json \
  | jq -r '.items[].spec.containers[].image' \
  | sort -u

套路 5:按 namespace 统计 pod 数

kubectl get pods -A -o json \
  | jq -r '.items[].metadata.namespace' \
  | sort | uniq -c | sort -rn

或者纯 jq:

kubectl get pods -A -o json \
  | jq -r '.items 
           | group_by(.metadata.namespace) 
           | map({ns: .[0].metadata.namespace, count: length}) 
           | .[] | "\(.count) \(.ns)"' \
  | sort -rn

后者纯 jq、不靠 sort/uniq。但前者更直觉、且更容易记住。

套路 6:journalctl JSON 解析

journalctl -u kubelet --since "10 min ago" -o json \
  | jq -r 'select(.PRIORITY|tonumber < 4) | "\(._SYSTEMD_UNIT): \(.MESSAGE)"'

套路 7:curl API 处理

# GitHub user info
curl -s https://api.github.com/users/torvalds \
  | jq '{name, repos: .public_repos, followers}'

# 拿 token
TOKEN=$(curl -s -X POST ... | jq -r '.token')

套路 8:组合修改 JSON

echo '{"name":"a","items":[1,2,3]}' \
  | jq '. + {extra: "yes"}'
# {"name":"a","items":[1,2,3],"extra":"yes"}

echo '{"name":"a","items":[1,2,3]}' \
  | jq '.items += [4,5]'
# {"name":"a","items":[1,2,3,4,5]}

echo '{"name":"a"}' \
  | jq '.name = "b"'
# {"name":"b"}

# 删字段
echo '{"a":1,"b":2}' | jq 'del(.a)'
# {"b":2}

多文档输入 / 输出

多个 JSON 用 --slurp / -s 合并成数组

echo '{"a":1}{"a":2}{"a":3}' | jq -s '.'
# [{"a":1},{"a":2},{"a":3}]

echo '{"a":1}{"a":2}{"a":3}' | jq -s 'map(.a) | add'
# 6

API 流式输出 / NDJSON 经常需要 -s 收成数组再处理。

紧凑输出 -c

echo '{"a":1,"b":[2,3]}' | jq -c '.'
# {"a":1,"b":[2,3]}                    ← 单行,不格式化

echo '[1,2,3]' | jq -c '.[]'
# 1
# 2
# 3

-c 在脚本里把 jq 输出再喂下一个工具时常用。

--arg 传入 shell 变量

NAME="kubelet"
kubectl get pods -A -o json | jq --arg n "$NAME" '.items[] | select(.metadata.name | contains($n))'

--arg name value 把 value 当字符串绑定到 jq 变量 $name。永远用 --arg 而不是字符串拼接——避免引号和注入问题。

COUNT=5
jq --argjson n "$COUNT" '.items[:$n]'              # 数字 / 布尔用 --argjson

常见踩坑

坑 1:忘了 -r,下游 shell 解释不对

NAME=$(kubectl ... | jq '.metadata.name')        # NAME='"my-pod"' 含引号
kubectl logs $NAME                                # 报错:pod "\"my-pod\"" not found

修:

NAME=$(kubectl ... | jq -r '.metadata.name')

坑 2:.[] 输出多个值,后续操作意外

echo '[{"a":1},{"a":2}]' | jq '.[] | length'
# 1 1                                              ← 对每个对象求 length

注意:.[] | f 是"对每个元素跑 f",不是"对整个数组跑 f"。要后者:

echo '[{"a":1},{"a":2}]' | jq 'length'           # 2

坑 3:字段名含特殊字符

echo '{"my-name":"k8s"}' | jq '.my-name'         # ❌ 解析成 (.my) - (name)
echo '{"my-name":"k8s"}' | jq '.["my-name"]'     # ✅
echo '{"my-name":"k8s"}' | jq '."my-name"'       # ✅

含 -、含数字开头、含空格的字段名,用 [".."] 语法访问。

坑 4:null 传播

echo '{}' | jq '.a.b.c'
# null                                             ← 不报错,直接 null

链式访问中间任何一步缺失,jq 默认返回 null(不报错)。但有些操作对 null 报错:

echo '{}' | jq '.a.b.c | length'                 # 报错:null has no length
echo '{}' | jq '.a.b.c // "default" | length'    # 用 // 给默认值 → 7

// 是默认值操作符(null / false 时取右边)。

坑 5:数字精度

echo '{"id":12345678901234567890}' | jq '.id'
# 12345678901234567000                            ← 大数被截断

jq 用 IEEE 754 双精度。超过 2^53 的整数会丢精度。要处理大整数(K8s 资源 ID、时间戳纳秒):

jq --raw-output '.id'                             # 当字符串处理

或者用 Python。

坑 6:jq 卡住

kubectl logs my-pod | jq '.'                      # 看着卡住

jq 在等输入结束才输出。如果 stdin 是流式(kubectl logs -f),jq 会缓冲不输出。解决:

kubectl logs -f my-pod | jq -c --unbuffered '.'
# 或 stdbuf -oL jq ...

坑 7:jq 语法太多想偷懒

如果你的 jq 表达式超过 3-4 行,考虑用 Python json.loads:

import json, sys
data = json.load(sys.stdin)
for p in data['items']:
    print(p['metadata']['name'])

更易调、易读。jq 在单行 shell 管道里最强;复杂逻辑请别硬怼。


一些有用的 alias

# .bashrc
alias jp='jq .'                  # pretty print
alias jc='jq -c .'               # compact
alias jr='jq -r .'               # raw output

# K8s 专用
alias kjq='kubectl get -o json'    # kubectl 输出 JSON

关联命令

  • grep / awk —— 文本类用这俩、JSON 用 jq
  • yq —— YAML 版的 jq,K8s manifest 处理常用
  • kubectl get -o jsonpath='...' —— kubectl 自带的 JSONPath(弱化版)
  • Python json —— jq 写不下时的退路
  • curl -s ... | jq —— REST API 探测套路
在 GitHub 上编辑此页