jq One-Liners Фильтрация и преобразование JSON из командной строки без написания скрипта

Обновлено

Семь практических рецептов jq для разработчиков backend и DevOps — охватывает select, map, to_entries, del, group_by и реальную перестройку API. Показаны входные и выходные данные для каждого шаблона.

jq One-Liners: Фильтрация и преобразование JSON из командной строки без написания скрипта 1
Реклама · УДАЛИТЬ?

Вы смотрите на сжатый ответ API в терминале. Вам нужно извлечь 3 поля, отфильтровать нулевые значения и направить результат в другое приложение. Написание скрипта на Python для этого требует 20 строк и 2 минуты, которых у вас нет. jq обрабатывает это за одну команду — если вы знаете соответствующие шаблоны.

Семь рецептов ниже. Каждый из них охватывает шаблон, который вы будете часто встречать при работе с ответами API или лог-файлами. Показаны входные данные, команда и выходные данные для каждого рецепта.

Установка

brew install jq        # macOS
apt-get install jq     # Ubuntu/Debian
apk add jq             # Alpine (Docker images)

Рецепт 1: Извлечение поля из каждого объекта в массиве

Наиболее распространённая операция, которую вы будете выполнять с jq: извлечение одного поля из каждого элемента в массиве JSON.

Входные данные (users.json)

[
  {"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"},
  {"id": 2, "name": "Bob",   "email": "bob@example.com",   "role": "user"},
  {"id": 3, "name": "Carol", "email": "carol@example.com", "role": "user"}
]
jq '.[].name' users.json
# → "Alice"
#   "Bob"
#   "Carol"  (newline-separated stream)

.[] проходит по каждому элементу в массиве; .name извлекает поле. Результат — поток, который полезен для передачи в другие команды. Если вам нужно получить корректный массив JSON обратно:

jq 'map(.name)' users.json
# → ["Alice", "Bob", "Carol"]

map(.name) сокращение означает [.[] | .name] — применяет выражение к каждому элементу и обёртывает результаты в массив. Вы будете часто использовать map() .

Рецепт 2: Фильтрация с помощью select

Оставляйте только объекты, соответствующие определённому условию; удаляйте остальные.

jq 'map(select(.role == "admin"))' users.json
[
  {"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"}
]

select(expr) передаёт элемент только тогда, когда выражение является истинным — все остальные исчезают. Обёртывание его в map() сохраняет выходной массив. Дополнительные примеры:

# Numeric comparison
jq 'map(select(.score > 80))' results.json

# Not-null check
jq 'map(select(.error != null))' events.json

# String contains (requires test)
jq 'map(select(.message | test("timeout")))' logs.json

Рецепт 3: Изменение структуры объектов с помощью map

Выбираете только необходимые поля и при необходимости переименовываете их в одном проходе.

jq 'map({name: .name, role: .role})' users.json
[
  {"name": "Alice", "role": "admin"},
  {"name": "Bob",   "role": "user"},
  {"name": "Carol", "role": "user"}
]

Вы создаёте новый объект для каждого элемента. Чтобы переименовать ключ, измените левую часть двоеточия:

jq 'map({username: .name, access_level: .role})' users.json

Одна сокращённая форма, которая экономит ввод: {name} эквивалентна {name: .name}. Если вы хотите, чтобы поле осталось без изменений, вам не нужно писать ключ дважды:

jq 'map({id, name, role})' users.json

Рецепт 4: Переименование ключей с помощью to_entries и from_entries

map подходит, когда вы знаете, какие ключи существуют. Когда вам нужно преобразовать сами ключи — добавить префикс, изменить соглашения о названиях или переименовать на основе соответствия — to_entries является правильным шаблоном.

Входные данные (config.json)

{"api_url": "https://api.example.com", "timeout": 30, "retry_count": 3}
# Add cfg_ prefix to every key
jq 'to_entries | map(.key = "cfg_" + .key) | from_entries' config.json
{"cfg_api_url": "https://api.example.com", "cfg_timeout": 30, "cfg_retry_count": 3}

to_entries преобразует {"k": "v"} в [{"key": "k", "value": "v"}]. После того, как вы изменяете элементы в получившемся массиве, from_entries восстанавливает объект. Чтобы переименовать один конкретный ключ без изменения других:

jq 'to_entries | map(if .key == "api_url" then .key = "endpoint" else . end) | from_entries' config.json

Рецепт 5: Удаление полей с помощью del

Противоположность изменению структуры: сохраняйте всё и удалите несколько конкретных полей. Основное применение — удаление чувствительных данных перед логированием или перед передачей нагрузки третьим сторонам.

jq 'map(del(.email))' users.json
[
  {"id": 1, "name": "Alice", "role": "admin"},
  {"id": 2, "name": "Bob",   "role": "user"},
  {"id": 3, "name": "Carol", "role": "user"}
]

Удалите несколько полей одновременно или удалите вложенные пути:

# Multiple top-level fields at once
jq 'del(.password, .token, .refresh_token)' user.json

# Nested path
jq 'del(.user.internal_id)' response.json

# All fields named "debug" at any depth (recursive descent)
jq 'del(.. | .debug? // empty)' response.json

Рецепт 6: Фильтрация, изменение структуры и сортировка за один проход

Реальная ценность — это последовательное использование этих примитивов. Ниже приведён шаблон, который часто встречается при работе с пагинированными ответами API: проникнуть в вложенный массив, отфильтровать его, изменить объекты и отсортировать результат.

Входные данные (repos.json)

{
  "total_count": 3,
  "items": [
    {"id": 1, "name": "repo-alpha", "stargazers_count": 142, "language": "Go",     "private": false},
    {"id": 2, "name": "repo-beta",  "stargazers_count": 89,  "language": "Python", "private": true},
    {"id": 3, "name": "repo-gamma", "stargazers_count": 310, "language": "Go",     "private": false}
  ]
}
jq '.items
  | map(select(.private == false and .language == "Go"))
  | sort_by(-.stargazers_count)
  | map({name, stars: .stargazers_count})' repos.json
[
  {"name": "repo-gamma", "stars": 310},
  {"name": "repo-alpha", "stars": 142}
]

Последовательность команд:

  • .items — проникнуть в вложенный массив
  • map(select(...)) — отфильтровать за один проход; and соединяет условия
  • sort_by(-.stargazers_count) — отрицает значение для сортировки в порядке убывания; sort_by(.field) одна операция даёт сортировку в порядке возрастания
  • map({name, stars: .stargazers_count}) — финальное изменение структуры; {name} сокращение означает {name: .name}

Рецепт 7: Подсчёт уровней логов с помощью group_by

Анализ частоты в структурированном выводе логов — без базы данных, без сложных операций с awk.

Входные данные (logs.json)

[
  {"level": "error", "msg": "connection timeout",     "service": "auth"},
  {"level": "info",  "msg": "request received",       "service": "api"},
  {"level": "error", "msg": "null pointer exception", "service": "worker"},
  {"level": "warn",  "msg": "slow query detected",    "service": "db"},
  {"level": "error", "msg": "rate limit exceeded",    "service": "api"}
]
jq 'group_by(.level) | map({level: .[0].level, count: length}) | sort_by(-.count)' logs.json
[
  {"level": "error", "count": 3},
  {"level": "info",  "count": 1},
  {"level": "warn",  "count": 1}
]

group_by(.level) возвращает массив из массивов — каждый подмассив содержит все записи, имеющие одинаковое значение уровня. .[0].level извлекает имя уровня из первого элемента группы; length подсчитывает количество записей в группе.

Добавьте разбивку по сервисам в том же запросе:

jq 'group_by(.level) | map({
  level:    .[0].level,
  count:    length,
  services: map(.service) | unique
})' logs.json

Скорое руководство

Таблица с описанием вышеуказанных шаблонов для случаев, когда вам нужно быстро напомнить:

Что вы хотитевыражение jq
Все значения поляmap(.field)
Фильтрация соответствующих элементовmap(select(.field == "val"))
Изменение структуры каждого объектаmap({newKey: .oldKey})
Переименование всех ключейto_entries | map(.key = ...) | from_entries
Удаление полейmap(del(.field1, .field2))
Сортировка по возрастаниюsort_by(.field)
Сортировка по убываниюsort_by(-.field)
Подсчёт по группамgroup_by(.field) | map({key: .[0].field, count: length})
Получение всех ключей объектаkeys
Подсчёт элементов в массивеlength
Уникальные значения поляmap(.field) | unique

Перед началом: Управляйте большим объёмом данных

Ответы API часто приходят в сжатом или глубоко вложённом виде. Если вы не уверены в нужном пути, вставьте JSON в Форматировщик JSON — он красиво отформатирует с сжатыми узлами, чтобы вы могли определить путь до написания выражения jq . После выполнения преобразования, инструмент полезен для проверки разницы до и после, когда вы изменяете или удаляете поля и хотите убедиться, что ничего не изменилось неожиданно. JSON-сравнение Эти семь шаблонов охватывают большую часть операций с JSON, которые вы будете выполнять в командной строке. Реальная сила — это последовательное использование этих шаблонов — как только вы сможете фильтровать, изменять структуру и сортировать, вы сможете обрабатывать большинство ответов API без обращения к скрипту.

jq One-Liners: Фильтрация и преобразование JSON из командной строки без написания скрипта 2

Хотите убрать рекламу? Откажитесь от рекламы сегодня

Установите наши расширения

Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска

в Расширение Chrome в Расширение края в Расширение Firefox в Расширение Opera

Табло результатов прибыло!

Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!

Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?

новости с техническими моментами

Примите участие

Помогите нам продолжать предоставлять ценные бесплатные инструменты

Купи мне кофе
Реклама · УДАЛИТЬ?