jq: обработка JSON в терминале с примерами

10 минут чтения
Средний рейтинг статьи — 4.8

jq — утилита для обработки JSON в терминале. Она форматирует нечитаемый ответ API, достаёт вложенные поля, фильтрует массивы, собирает новые объекты, считает агрегаты и превращает JSON в строки для скриптов. Если вы работаете с curl, логами, Docker, Kubernetes или REST API, jq быстро становится такой же базовой командой, как grep, awk и sed.

Идея простая: на вход jq получает JSON, на выход отдаёт результат применения фильтра — от тривиального «покажи поле status» до сложного «возьми все проверки с кодом не 200, сгруппируй по региону и выведи компактный список». Ниже — примеры: установка, синтаксис, работа с объектами и массивами, API, JSON Lines, опции для скриптов и типовые рецепты для разработчиков и DevOps-инженеров.

Что такое jq и когда он нужен

jq часто называют «sed для JSON», но аналогия неполная. sed работает со строками, а jq понимает структуру JSON: объекты, массивы, строки, числа, true, false, null. Поэтому вместо хрупких регулярных выражений можно обращаться к данным по пути: .user.email, .items[], .checks[] | select(.status != "ok").

Типовые задачи для jq:

1) Быстро посмотреть ответ API — многие API отдают минифицированный JSON в одну строку, а jq '.' форматирует его с отступами и подсветкой в терминале.

2) Достать нужное поле — например, код статуса, ID задачи, токен из ответа, версию релиза, IP контейнера.

3) Отфильтровать массив объектов — найти пользователей с ролью admin, контейнеры в статусе running, проверки с ошибкой, записи логов с уровнем error.

4) Подготовить данные для скрипта — вывести значение без кавычек через -r, проверить условие через -e, собрать TSV/CSV-подобный вывод.

5) Диагностировать сервисы — jq удобно использовать вместе с curl, journalctl, docker inspect, kubectl, gh, aws и другими CLI, которые возвращают JSON.

Если вы уже используете текстовые утилиты, посмотрите также материал про практическую работу с awk. awk хорош для табличного текста и логов, а jq — для структурированного JSON.

Установка jq и базовый синтаксис

В большинстве Linux-дистрибутивов jq ставится из стандартного репозитория.

# Debian, Ubuntu
sudo apt update
sudo apt install jq
 
# Fedora
sudo dnf install jq
 
# Arch Linux
sudo pacman -S jq
 
# macOS через Homebrew
brew install jq

Проверить версию:

jq --version

Базовый формат команды:

jq 'фильтр' файл.json

Если JSON приходит из другой команды, используйте pipe:

curl -s https://api.github.com/repos/jqlang/jq | jq '.'

Фильтр . означает «вернуть весь входной JSON» — это самый простой способ красиво отформатировать документ:

jq '.' response.json

Допустим, есть файл service.json:

{
  "service": "api",
  "status": "ok",
  "version": "1.8.3",
  "checks": [
    { "name": "database", "status": "ok", "latency_ms": 12 },
    { "name": "redis", "status": "ok", "latency_ms": 3 },
    { "name": "payments", "status": "degraded", "latency_ms": 850 }
  ]
}

Получить поле верхнего уровня:

jq '.status' service.json

Результат:

"ok"

По умолчанию строки выводятся как JSON-строки — с кавычками. Для shell-скриптов чаще нужен «сырой» вывод:

jq -r '.status' service.json

Результат:

ok

Если нужно получить несколько полей, можно собрать новый объект:

jq '{service: .service, status: .status, version: .version}' service.json

Или вывести строку:

jq -r '"\(.service) \(.version): \(.status)"' service.json

Здесь \(...) — интерполяция выражений внутри строки jq.

Навигация по объектам и массивам

Обращение к полям в jq похоже на JavaScript, но это отдельный язык фильтров.

Получить вложенное поле:

jq '.user.profile.email' user.json

Если ключ содержит дефис, пробел или начинается с цифры, используйте квадратные скобки:

jq '.["content-type"]' headers.json
jq '.user["display name"]' user.json

Работа с массивами:

jq '.checks' service.json
jq '.checks[0]' service.json
jq '.checks[0].name' service.json

Индексация начинается с нуля. Отрицательный индекс берёт элементы с конца:

jq '.checks[-1]' service.json

Чтобы пройти по всем элементам массива, используется []:

jq '.checks[]' service.json

Получить имена всех проверок:

jq -r '.checks[].name' service.json

Результат:

database
redis
payments

Можно брать срезы:

jq '.checks[0:2]' service.json

Это вернёт первые два элемента.

Полезные функции для объектов и массивов:

jq '.checks | length' service.json
jq 'keys' service.json
jq 'has("status")' service.json
jq '.checks | map(.name)' service.json

length возвращает длину массива, строки или количество ключей объекта. keys показывает ключи объекта. map(...) применяет выражение к каждому элементу массива и возвращает новый массив.

Если поле может отсутствовать, пригодится безопасный оператор ?:

jq '.user?.email' response.json

А если нужно значение по умолчанию, используйте //:

jq -r '.user.email // "unknown@example.com"' response.json

Это удобно при работе с API, где часть полей может быть null или вовсе отсутствовать в ответе.

Фильтрация и преобразование данных

Сильная сторона jq — фильтры и пайплайны. Как в shell, оператор | передаёт результат одного выражения следующему.

Вывести проверки, у которых статус не ok:

jq '.checks[] | select(.status != "ok")' service.json

Получить только имена таких проверок:

jq -r '.checks[] | select(.status != "ok") | .name' service.json

Фильтр по числовому значению:

jq '.checks[] | select(.latency_ms > 100)' service.json

Собрать новый объект из отфильтрованных данных:

jq '.checks[] | select(.latency_ms > 100) | {name, status, latency_ms}' service.json

Если имя поля в новом объекте совпадает с исходным, можно писать коротко: {name, status}. Если нужно переименовать поле:

jq '.checks[] | {check: .name, state: .status, latency: .latency_ms}' service.json

Для преобразования массива целиком используйте map:

jq '.checks | map({name, ok: (.status == "ok")})' service.json

Результат:

[
  {
    "name": "database",
    "ok": true
  },
  {
    "name": "redis",
    "ok": true
  },
  {
    "name": "payments",
    "ok": false
  }
]

Сортировка:

jq '.checks | sort_by(.latency_ms)' service.json

Максимальное значение:

jq '.checks | max_by(.latency_ms)' service.json

Сумма задержек:

jq '.checks | map(.latency_ms) | add' service.json

Среднее значение можно посчитать так:

jq '.checks | (map(.latency_ms) | add) / length' service.json

group_by сам сортирует элементы по значению ключа перед группировкой, поэтому отдельный sort_by не нужен:

jq '.checks | group_by(.status) | map({status: .[0].status, count: length})' service.json

jq вместе с curl и HTTP API

Самый частый сценарий — проверить API через curl и сразу разобрать ответ. Если вы редко используете curl, начните с гайда что такое curl и как им пользоваться.

Пример: получить информацию о репозитории GitHub и вывести только нужные поля:

curl -s https://api.github.com/repos/jqlang/jq \
  | jq '{name, description, stars: .stargazers_count, forks}'

Флаг -s отключает прогресс-бар curl, чтобы он не мешал JSON. В скриптах часто добавляют -S и -f:

curl -sSf https://example.com/health | jq '.'

-f заставляет curl завершиться с ошибкой при HTTP-кодах 400 и выше. Это удобно в проверках, но помните: тело ответа при ошибке может не попасть в пайп.

Проверить health endpoint и вернуть код завершения в зависимости от JSON-поля:

curl -sSf https://example.com/health \
  | jq -e '.status == "ok"' > /dev/null

Опция -e делает jq полезным для shell-условий: если результат false или null, команда завершится с ненулевым кодом.

if curl -sSf https://example.com/health | jq -e '.status == "ok"' > /dev/null; then
  echo "service is healthy"
else
  echo "service is unhealthy"
fi

Такой скрипт не заменяет внешний мониторинг доступности: он выполняется только там, где вы его запустили, и не видит проблему со стороны пользователя. Для публичных сайтов обычно нужна отдельная проверка с заданным интервалом — например, Statuser, который регулярно обращается к сайту и присылает уведомление при сбое. А jq полезен на этапе диагностики: быстро посмотреть, что именно вернул endpoint.

Если предпочитаете более читаемый CLI для API, посмотрите статью HTTPie вместо curl. HTTPie тоже хорошо сочетается с jq, если нужно дополнительно фильтровать JSON.

Переменные, аргументы и безопасная подстановка

Новички часто пытаются склеивать фильтр jq строками shell. Это опасно: можно сломать кавычки или получить некорректный JSON. Для передачи значений используйте --arg и --argjson.

Фильтр по имени, которое хранится в переменной shell:

CHECK_NAME="payments"
 
jq --arg name "$CHECK_NAME" \
  '.checks[] | select(.name == $name)' \
  service.json

--arg всегда передаёт значение как строку. Если нужно число, boolean или объект, используйте --argjson:

LIMIT=100
 
jq --argjson limit "$LIMIT" \
  '.checks[] | select(.latency_ms > $limit)' \
  service.json

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

STATUS="degraded"
LIMIT=500
 
jq --arg status "$STATUS" --argjson limit "$LIMIT" \
  '.checks[] | select(.status == $status and .latency_ms > $limit)' \
  service.json

Создать JSON из shell-переменных:

SERVICE="api"
STATUS="ok"
VERSION="1.8.3"
 
jq -n --arg service "$SERVICE" --arg status "$STATUS" --arg version "$VERSION" \
  '{service: $service, status: $status, version: $version}'

Опция -n запускает jq без входного JSON. Это удобно, когда нужно сгенерировать JSON-объект в скрипте, например для webhook-запроса или тестового payload.

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

JSON Lines, логи и большие файлы

Не все JSON-данные хранятся одним документом. В логах часто используется формат JSON Lines: каждая строка — отдельный JSON-объект.

Пример app.log:

{"ts":"2026-01-10T10:00:01Z","level":"info","msg":"started","request_id":"a1"}
{"ts":"2026-01-10T10:00:03Z","level":"error","msg":"database timeout","request_id":"b2"}
{"ts":"2026-01-10T10:00:04Z","level":"warn","msg":"slow response","request_id":"c3","latency_ms":1200}

jq умеет читать такой файл построчно:

jq 'select(.level == "error")' app.log

Вывести компактно, по одному объекту на строку:

jq -c 'select(.level != "info")' app.log

Опция -c включает compact output. Она полезна, когда результат снова должен быть JSON Lines.

Сделать читаемый вывод:

jq -r 'select(.level != "info") | "\(.ts) [\(.level)] \(.msg)"' app.log

Результат:

2026-01-10T10:00:03Z [error] database timeout
2026-01-10T10:00:04Z [warn] slow response

Если логи идут из journalctl, можно получать JSON и фильтровать его через jq:

journalctl -u nginx.service -o json \
  | jq -r 'select(.PRIORITY <= "3") | "\(.__REALTIME_TIMESTAMP) \(.MESSAGE)"'

У journalctl своя специфика форматов и полей, поэтому для системных логов пригодится отдельный разбор: анализ логов с помощью journalctl.

Для больших JSON-массивов обычный jq '.items[]' file.json может потреблять много памяти, потому что документ нужно разобрать целиком. В таких случаях лучше, если источник отдаёт JSON Lines. Если это невозможно, посмотрите в сторону потокового режима --stream, но он сложнее: jq будет выдавать пары «путь — значение», а не привычные объекты. Для большинства прикладных задач достаточно JSON Lines и jq -c.

Ещё одна полезная опция — -s, или slurp. Она читает все входные JSON-документы и складывает их в массив:

jq -s 'group_by(.level) | map({level: .[0].level, count: length})' app.log

Это удобно для агрегаций, но на больших логах может съесть много памяти.

Практические рецепты для DevOps и разработки

Ниже — команды, которые часто встречаются в реальной работе.

1) Проверить, что API вернул нужную версию

curl -sSf https://example.com/version \
  | jq -e -r '.version == "1.8.3"' > /dev/null

Такую проверку можно встроить в deploy-скрипт после выкладки. Если релиз связан с HTTP API, полезно также понимать, как устроены health checks: об этом есть материал проверка состояния сервисов в Node.js.

2) Получить IP-адрес Docker-контейнера

docker inspect my-container \
  | jq -r '.[0].NetworkSettings.Networks | to_entries[0].value.IPAddress'

docker inspect возвращает массив, поэтому сначала берём .[0]. Если нужно разобраться с базовыми Docker-командами, пригодится статья основные команды Docker.

3) Показать имена контейнеров и их статусы из JSON

docker ps --format '{{json .}}' \
  | jq -r '"\(.Names)\t\(.Status)\t\(.Ports)"'

Здесь Docker отдаёт JSON Lines: по одному объекту на контейнер.

4) Найти неуспешные проверки в отчёте мониторинга

jq -r '
  .checks[]
  | select(.http_status != 200 or .ok == false)
  | "\(.name): status=\(.http_status), error=\(.error // "none")"
' report.json

Это удобно при разборе инцидента — быстро получить список проблемных URL, регионов или backend-проверок из сохранённого JSON-отчёта.

5) Преобразовать массив объектов в TSV

jq -r '.checks[] | [.name, .status, .latency_ms] | @tsv' service.json

Результат можно передать в column:

jq -r '.checks[] | [.name, .status, .latency_ms] | @tsv' service.json | column -t

6) Достать уникальные значения

jq '.checks | map(.status) | unique' service.json

Для строкового вывода:

jq -r '.checks | map(.status) | unique[]' service.json

7) Удалить чувствительные поля перед отправкой лога

jq 'del(.password, .token, .headers.authorization)' request.json

Если нужно удалить поле в каждом элементе массива:

jq '.users | map(del(.password_hash, .totp_secret))' users.json

8) Объединить несколько JSON-файлов

jq -s 'add' part1.json part2.json part3.json

Для объектов add объединит ключи. Для массивов — склеит массивы. Если ключи объектов совпадают, значение из более позднего файла перезапишет предыдущее.

9) Отфильтровать Kubernetes pods по фазе

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

10) Получить последние версии релизов из GitHub API

curl -s https://api.github.com/repos/jqlang/jq/releases/latest \
  | jq -r '.tag_name'

Для автоматизации этого обычно добавляют обработку HTTP-ошибок, лимитов API и таймаутов на стороне curl.

Частые ошибки и полезные опции jq

jq лаконичен, но у него есть несколько мест, где легко ошибиться.

1) Забыли -r и получили кавычки

TOKEN=$(jq '.token' response.json)

В переменную попадёт строка с кавычками. Правильно:

TOKEN=$(jq -r '.token' response.json)

2) Перепутали массив и объект

Если команда .items.name возвращает null, возможно, items — массив. Нужно пройти по элементам:

jq '.items[].name' response.json

3) Поле отсутствует, и фильтр ломается дальше по пайплайну

Используйте ? и //:

jq -r '.user?.email // "no-email"' response.json

4) Shell съел кавычки

Фильтр jq почти всегда лучше заключать в одинарные кавычки:

jq '.items[] | select(.status == "ok")' file.json

Если внутри нужно значение из shell-переменной, не вставляйте его напрямую — используйте --arg.

5) Нужен код завершения, а не текст

Для проверок в CI/CD и shell-скриптах используйте -e:

jq -e '.ok == true' response.json > /dev/null

6) Нужно отключить цветной вывод

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

jq -M '.' response.json

7) Нужно сохранить порядок и компактность

Компактный JSON:

jq -c '.' response.json

Отсортировать ключи объектов:

jq -S '.' response.json

8) Нужно обработать невалидный JSON

jq строгий: если вход не является валидным JSON, он завершится с ошибкой. Это плюс для скриптов, потому что проблема обнаруживается сразу. Если команда перед jq иногда возвращает HTML-страницу ошибки вместо JSON, сначала проверьте HTTP-код и заголовки. Для диагностики сетевых и HTTP-проблем могут пригодиться материалы про ошибку 502 Bad Gateway и все коды ответов HTTP.

FAQ

Можно ли редактировать JSON-файл через jq на месте?

У jq нет встроенного режима -i, как у sed. Обычно пишут во временный файл, а потом заменяют исходный:

jq '.version = "1.8.4"' config.json > config.tmp && mv config.tmp config.json

Чем jq отличается от grep для JSON?

grep ищет текст, а jq понимает структуру JSON. Через jq можно безопасно выбрать поле .user.email, отфильтровать массив, проверить тип значения и собрать новый JSON.

Можно ли использовать jq в CI/CD?

Да. Частый сценарий — проверить ответ API, достать версию, прочитать JSON-отчёт тестов или провалить job через jq -e, если условие не выполнено.

Что делать, если JSON слишком большой?

Лучше получать данные в формате JSON Lines и обрабатывать построчно. Для огромных единых JSON-документов можно изучить jq --stream, но его фильтры сложнее обычных.

jq стоит держать в стандартном наборе терминальных инструментов. Он закрывает промежуток между «быстро посмотреть ответ API» и «писать отдельный скрипт на Python или Node.js». Для разовых проверок, диагностики инцидентов, обработки логов и автоматизации деплоя это один из самых полезных CLI-инструментов.

Опубликовано 10 ноября 202610 минут чтенияАнастасия Левина
Средний рейтинг статьи — 4.8

Настроить мониторинг за 30 секунд

Надёжные уведомления о даунтаймах. Без ложных срабатываний