yq —— YAML / JSON / XML 的 jq

运维工具手册 04-文本处理 5.5 KB 原文

yq —— YAML / JSON / XML 的 jq

分类:04 文本处理 适用:全平台。注意有两个同名工具,语法不兼容 一句话:改 K8s 清单、Ansible 变量、CI 配置,比 sed 安全,比手改可靠。

1. 先确认你装的是哪个 yq

yq --version
版本 特征 语法
mikefarah/yq(Go,v4) 输出含 mikefarah/yq yq '.a.b' file.yml(类 jq)
kislyuk/yq(Python) 输出 yq 3.x(基于 PyYAML) yq -y '.a.b'(需 -y 输出 YAML)

本文以 mikefarah v4 为准(当前主流,单二进制,无依赖)。装错版本会出现"命令照着敲却报错"。

# 安装 v4
curl -L https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -o /usr/local/bin/yq
chmod +x /usr/local/bin/yq
# 或
apt install -y yq      # 注意部分仓库是老版本
brew install yq

2. 基础

yq '.a.b' file.yml                 # 读取(输出 YAML)
yq -r '.a.b' file.yml              # 原始字符串(无引号)
yq -o=json '.' file.yml            # YAML → JSON
yq -P '.' file.json                # JSON → YAML
yq '.items[0].name' file.yml
yq '.spec.containers[].image' deploy.yml
yq '.[]' list.yml                  # 展开数组
yq 'keys' file.yml
yq 'length' file.yml
yq '.a | .b' file.yml              # 管道

原地修改

yq -i '.replicas = 3' deploy.yml            # -i 直接写回
yq -i '.image = "app:1.2.3"' deploy.yml
yq -i 'del(.metadata.annotations)' deploy.yml
yq -i '.spec.template.spec.containers[0].image |= sub(":.*"; ":1.2.3")' deploy.yml

-i 前先复制或 git diff,YAML 的缩进/注释处理在不同版本有差异。

3. 常用操作

读

yq '.metadata.name' k8s.yml
yq '.spec.template.spec.containers[].image' k8s.yml
yq '.data | keys' cm.yml
yq '.items[] | select(.kind == "Deployment") | .metadata.name' all.yml
yq '.[] | select(.enabled == true)' list.yml
yq '.env[] | select(.name == "DB_HOST") | .value' deploy.yml

改

yq -i '.a.b = "x"' f.yml                     # 赋值
yq -i '.a.b |= . + 1' f.yml                  # 基于原值修改
yq -i '.a.b += ["new"]' f.yml                # 数组追加
yq -i '.spec.replicas = 5' f.yml
yq -i '(.spec.template.spec.containers[] | select(.name == "app") | .image) = "app:2.0"' f.yml
yq -i 'del(.status)' f.yml
yq -i '.metadata.labels.team = "infra"' f.yml
yq -i '.spec.template.metadata.annotations."prometheus.io/scrape" = "true"' f.yml

合并

yq '.a * .b' a.yml b.yml            # 深合并(后者覆盖前者)
yq ea '. as $item ireduce ({}; . * $item)' a.yml b.yml    # 多文档合并
yq -s '.' a.yml b.yml               # 合成数组

多文档(--- 分隔)

yq -N '.kind' multi.yml             # 每个文档都取
yq 'select(.kind == "Service")' multi.yml
yq -i 'select(.kind == "Deployment") | .spec.replicas = 2' multi.yml
yq ea '[.] | length' multi.yml      # 文档数

4. 输出控制

yq -o=json '.' f.yml                # 转 JSON
yq -o=props '.' f.yml               # properties
yq -o=xml '.' f.xml                 # XML 输出
yq -P '.' f.json                    # 强制 YAML 输出
yq -r '.a' f.yml                    # 无引号
yq -j '.' f.yml                     # 紧凑 JSON(无缩进)
yq -I4 '.' f.yml                    # 缩进 4 空格
yq '.. f.yml          # 流式风格
yq -e '.a == "x"' f.yml             # 断言,false 时退出码 1(CI 用)

5. 变量与 env

yq '.image = strenv(IMAGE_TAG)' deploy.yml          # 读环境变量
yq --arg v "1.2.3" '.image = "app:" + $v' deploy.yml
yq --argjson r 3 '.replicas = $r' deploy.yml

CI 里非常实用:

IMAGE_TAG="$CI_COMMIT_SHA" yq -i '.spec.template.spec.containers[0].image = "registry/app:" + strenv(IMAGE_TAG)' deploy.yml

6. 实战场景

6.1 批量改 K8s 镜像版本(发布脚本)

#!/bin/bash
set -euo pipefail
NEW_TAG="$1"
for f in k8s/*.yaml; do
  yq -i '(.spec.template.spec.containers[] | select(.name == "app") | .image) |= sub(":[^:]+$"; ":'"$NEW_TAG"'")' "$f"
done
kubectl diff -f k8s/ || true

6.2 从 compose 文件提取所有镜像

yq -r '.services[].image' docker-compose.yml | sort -u

6.3 检查 K8s 清单是否都设置了 resources

yq -e 'select(.kind == "Deployment") | .spec.template.spec.containers[] | has("resources")' deploy.yml

6.4 YAML ↔ JSON 互转

yq -o=json '.' config.yml > config.json
yq -P '.' config.json > config.yml

6.5 修改 Ansible 变量

yq -i '.nginx_port = 8080' group_vars/all.yml
yq -i '.packages += ["htop"]' group_vars/all.yml

6.6 对比两个 YAML 的差异(转成规范 JSON 再 diff)

diff <(yq -o=json -P '.' a.yml) <(yq -o=json -P '.' b.yml)

7. 常见坑

  1. 装成了 kislyuk 版:yq '.a' f.yml 会输出 JSON 而不是 YAML,需要 -y。先 yq --version 确认。
  2. -i 会重排格式:注释、空行、引号风格可能变化,重要文件先 git diff。
  3. 锚点与别名(& / *):v4 默认展开,需要 --tag '!!merge' 或 explode(.) 处理合并键。
  4. 键名含特殊字符:.metadata.annotations."prometheus.io/scrape" 必须加引号。
  5. 数字被当字符串:"8080" 与 8080 不同,K8s 里类型错会导致 apply 失败,用 --argjson。
  6. 布尔值大小写:YAML 1.1 里 yes/no/on/off 也算布尔,v4 按 1.2 处理,可能与旧配置不一致。
  7. 多文档不加 -N 会报错:yq '.a' multi.yml 只处理第一个文档,要遍历用 yq ea 或 -N。
  8. select 后赋值不生效:必须用括号包住整个路径 (... | select(...)) = x。
  9. -i 与管道 stdin 混用:cat f | yq -i ... 会失败,-i 只接受文件参数。
  10. XML 支持有限:属性用 +@attr 访问,复杂 XML 用 xmlstarlet。
  11. 大文件性能:v4 全量加载,几十 MB 的 YAML 会慢,尽量避免。

8. 速查表

yq --version                                  # 先确认版本
yq '.a.b' f.yml                               # 读
yq -r '.a.b' f.yml                            # 裸字符串
yq -i '.a.b = "x"' f.yml                      # 原地改
yq -i 'del(.a)' f.yml                         # 删
yq -i '.list += ["x"]' f.yml                  # 数组追加
yq '.items[] | select(.k == "v")' f.yml       # 过滤
yq -o=json '.' f.yml                          # 转 JSON
yq -P '.' f.json                              # 转 YAML
yq -N '.kind' multi.yml                       # 多文档
yq --arg v x '.a = $v' f.yml                  # 变量
yq '.a = strenv(ENV)' f.yml                   # 环境变量
yq -e '.a == 1' f.yml                         # CI 断言
yq '.a * .b' a.yml b.yml                      # 深合并
yq —— YAML / JSON / XML 的 jq - 知识库 - 起航社区