yq
Чтение и правка YAML в стиле jq — выборка, select, правка на месте, слияние файлов, конвертация в JSON
yq (версия mikefarah, v4) — это jq для YAML: тот же язык выражений, но читает и пишет YAML, а ещё JSON, XML, TOML, CSV, properties. Главные отличия от jq — умеет править файл на месте (-i) и сохраняет комментарии и форматирование.
Синтаксис
yq [опции] 'выражение' [файл…]
yq eval 'выражение' файл # то же самое: eval (e) — команда по умолчанию
yq eval-all 'выражение' файл… # (ea) все файлы и документы сразу, одним потоком
Без файла читает stdin. Выражение бери в одинарные кавычки.
Опции
| Опция | Что делает |
|---|---|
-i | записать результат обратно в (первый) файл |
-o=ФОРМАТ | формат вывода: yaml, json, xml, props, csv, tsv, shell (вывод в TOML в v4.44 поддерживается только для простых значений) |
-p=ФОРМАТ | формат входа: yaml, json, xml, props, csv, tsv, toml; по умолчанию угадывается по расширению |
-I=N | отступ; -o=json -I=0 — JSON в одну строку |
-P | «красивый» YAML: блочный стиль вместо {…} и […] |
-r | печатать строки без кавычек и комментариев — включено по умолчанию для YAML; -r=false — выключить |
-e | код выхода 1, если результат null, false или ничего не найдено |
-n | не читать вход — создать документ с нуля |
-N | не печатать разделители документов --- |
-C / -M | принудительно включить / выключить цвет |
--from-file файл | взять выражение из файла |
-f extract | работать с YAML-шапкой (front matter) markdown-файла |
-s 'выражение' | разложить результаты по файлам, имя файла — из выражения |
Флагов --arg, как у jq, нет: значения из оболочки передавай через окружение (strenv, env).
Пути и выборка
| Выражение | Что делает |
|---|---|
.a.b, .a[0], .a[-1], .a[] | поле, элемент, последний, все элементы |
.["a.b"] | ключ с точкой или спецсимволами |
.. | рекурсивно все узлы |
select(.name == "x") | отбор по условию; есть and, or, not |
has("k"), keys, length | наличие ключа, список ключей, длина |
tag | тип узла: !!str, !!int, !!bool, !!map, !!seq, !!null |
path, key, parent | путь к узлу, его ключ или индекс, родительский узел |
a // b | b, если a — null, false или ничего |
select(di == 1) | второй документ из файла с несколькими --- (di = document_index) |
Изменение
| Выражение | Что делает |
|---|---|
.a = "v" | присвоить; промежуточные ключи создаются сами |
.a |= f | обновить значение через выражение: .name |= upcase |
(… | select(…) | .f) = "v" | изменить поле только у подходящих элементов, сохранив весь документ |
.list += ["x"] | дописать в массив |
del(.a) / del(.list[] | select(…)) | удалить поле / подходящие элементы |
. * {"a": 1} | глубокое слияние объектов (правый перекрывает левый); *+ — массивы склеиваются |
.a style="double" | стиль записи значения: double, single, literal (|), folded (>), flow |
... comments = "" | убрать все комментарии |
explode(.) | раскрыть якоря и ссылки (&a, *a, <<:) |
sort_keys(..) | отсортировать ключи на всех уровнях |
Функции
| Функции | Что делают |
|---|---|
strenv(VAR) / env(VAR) | значение переменной окружения: всегда строкой / с разбором типа |
envsubst | подставить ${VAR} внутри строки |
load("файл") | загрузить другой YAML-файл |
map(f), to_entries, from_entries, with_entries(f) | как в jq |
sort, sort_by(f), unique, group_by(f), reverse | сортировка и группировка |
split(s), join(s), upcase, downcase, test(re), sub(re; s) | строки и регулярки |
line_comment, head_comment, foot_comment | прочитать или задать комментарий узла |
@json, @yaml, @base64, @base64d | закодировать значение в строку другого формата |
Примеры
cat > app.yaml <<'EOF'
app:
name: demo
replicas: 2
env:
- name: MODE
value: dev
EOF
# прочитать значение
yq '.app.replicas' app.yaml
# найти элемент списка по полю
yq '.app.env[] | select(.name == "MODE") | .value' app.yaml
# показать изменение, не трогая файл
yq '.app.replicas = 3' app.yaml
# изменить на месте
yq -i '.app.replicas = 3' app.yaml
# поменять значение у конкретного элемента списка
yq -i '(.app.env[] | select(.name == "MODE") | .value) = "test"' app.yaml
# добавить вложенное поле — промежуточные ключи создадутся сами
yq -i '.app.labels.tier = "web"' app.yaml
# значение из переменной окружения как строку
NAME=demo2 yq '.app.name = strenv(NAME)' app.yaml
# YAML → JSON и дальше в jq
yq -o=json '.app' app.yaml | jq -r '.name'
# JSON → YAML
echo '{"k":[1,2]}' | yq -p=json -o=yaml
# слить два файла: значения из override.yaml перекрывают base.yaml
yq ea '. as $item ireduce ({}; . * $item)' base.yaml override.yaml
# все значения плоским списком «путь = значение»
yq -o=props app.yaml
# создать документ с нуля
yq -n '.server.port = 8080'
Подводные камни
- Есть два разных
yq. Python-версия (kislyuk/yq) — обёртка надjqс другими флагами (например,-yдля вывода YAML) и без сохранения комментариев. Проверьyq --version: нужна строкаmikefarah/yq … v4. - Без
-iфайл не меняется — результат просто печатается. Аyq '…' f.yaml > f.yamlобнулит файл, как и с любой командой. - Присваивание после
|меняет весь документ..app.env[] | select(…) | .value = "x"выведет только выбранные элементы. Чтобы получить весь файл с правкой, бери левую часть в скобки:(… | .value) = "x". - Типы важны.
.version = 1.10запишет число, а.version = "1.10"— строку. Для значений из окружения:strenv()всегда даёт строку,env()разбирает тип (5станет числом). - Несуществующий путь даёт
null, а не ошибку. Для проверок добавляй-e. - Несколько документов в файле. Выражение применяется к каждому документу отдельно, вывод разделяется
---. Чтобы работать с ними вместе (собрать в массив, слить), нуженeval-all. *сливает словари, но массивы заменяет целиком. Чтобы склеить списки, используй*+.- Строку
"false"в кавычках YAML считает строкой, аfalseбез кавычек — булевым значением; то же сyes/no,on/offв YAML 1.1-парсерах других программ. Если значение должно остаться строкой, держи кавычки.
См. также
jq · kubectl · ansible · docker-compose · gitlab-ci