Перейти к содержанию

jq

jq позволяет работать с json из консоли. На jqplay.org можно дебажить скрипт, выдаёт строку для вставки в command line.

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

jq 'тут вставить jq код' file

Примеры запросов

  • построчное чтение и обработка файла, где каждая строка — корректная json строка

    jq < file.json
    cat file.json | jq
    jq '' file.json
    jq '.' file.json
    jq --from-file filename.jq file.json
    

  • -c, --compact-output — вывод результата jq в одну строку

    jq -c '' file.json
    

  • вывод одного поля

    # если каждая запись - объект
    jq '.message' file.json
    
    # если каждая запись - массив
    jq '.[] | .name' list.json
    

  • фильтрация списка json-объектов по значению поля

    select(.elementId == "2010004571")
    .[] | select((.elementId == "2010004571") and (.id > 2))
    

  • собрать новый объект. Можно использовать краткое определение (shorter notation)

    { http_code, time_total }
    { newFieldName: .name, id: .id }
    { newFieldName: .name, id }
    
    # обработка массива на входе
    .[] | { http_code: .status, time_total }
    

Пояснения по работе jq

jq читает входные данные как набор json, разделённых пробелами. json может быть как объект, там и коллекция объектов.

Пример файла, где каждая запись — объект: ```json lines {"id": 1, "name": "userName 1"} {"id": 2, "name": "userName 2"} {"id": 3, "name": "userName 3"}
</details>

<details>
<summary>Пример файла, где каждая запись — массив объектов:</summary>

```json lines
[{"id": 1, "productName": "product 1"}, {"id": 11, "productName": "product 2"}]
[{"id": 2, "productName": "product 1"}, {"id": 22, "productName": "product 2"}]
[{"id": 3, "productName": "product 1"}, {"id": 33, "productName": "product 2"}]

К каждой записи jq применяет фильтр. Соответственно надо учитывать, какой тип будет на входе — объект или массив.

Самый простой фильтр — . — он просто отправляет данные дальше.

Следующий фильтр .foo — получает значение по ключу и возвращает его. Такой вариант сработает, если на входе объект, а не массив. Если ключа не существует - бросит ошибку

# в файле file.json каждая строка - json объект
# {"id": 123, "name": "value"}
.name

Фильтр .[] — возвращает все элементы массива. Если надо применить фильтр к каждому значению массива — надо использовать оператор |

# в файле каждая строка - json массив объектов
# [{"id": 123, "name": "value 1"}, {"id": 123, "name": "value 2"}, {"id": 123, "name": "value 3"}]
.[] | .name

Формирование объектов и массивов на выходе

Чтоб на выходе был массив или объект — используются конструкции jq '[]' и jq '{}'

Например, если надо собрать новый объект по каждой записи, где каждая запись — массив объектов, то делаем так:

  • кажудю запись читаем как массив
  • далее через пайп направляем на конструктор объекта с указанием имени поля fieldNewName и фильтром .name, который вытащит значение из очередного объекта
    .[] | { fieldNewName: .name }
    

Фильтрация данных

Для фильтрации данных используется функция select. Она не меняет данные. Она либо отправляет текущие данные дальше, либо не делает ничего.

Пример фильтрации входящих данных, а потом формирования новых объектов:

# если каждая запись - объект
select(.name == "value") | { auto: .name }

# если каждая запись - массив объектов
.[] | select(.name == "value") | { auto: .name }

select поддерживает логические операторы:

select((.id > 1) and (.id < 4))

Определение типа данных

С помощью функции type можно запросить тип очередной записи. По каждой записи она выведет "object" либо "array":

jq 'type' file.json

Различные примеры

Работа с логами, содержащими json текст

На входе:
[2023-06-30 11:11:11] INFO []: Sending: {"Items":[{"ID":"11","Value":1},{"ID":"12","Value":1},{"ID":"13","Value":1}],"Password":"Pass"}
[2023-06-30 12:12:12] INFO []: Sending: {"Items":[{"ID":"22","Value":1}],"Password":"Pass"}
[2023-06-30 21:21:21] INFO []: Sending: {"Items":[{"ID":"33","Value":1}],"Password":"Pass"}

Необходимо извлечь дату и добавить её внутрь json, вернуть новый объект

ltrimstr("[") | split("] INFO []: Sending: ") | .[0] as $date | .[1] | fromjson | .Items[].date = $date | .Items[]

С помощью split разбиваем строку в массив. С помощью Variable / Symbolic Binding Operator: ... as $identifier сохраняем дату в переменную. С помощью fromjson перегоняем json в объект. Далее расширяем нужные нам объекты с помощью переменной.

Вызов:

jq --compact-output --raw-input 'тут вставить код' file.log

На выходе: ```json lines {"ID":"11","Value":1,"date":"2023-06-30 11:11:11"} {"ID":"12","Value":1,"date":"2023-06-30 11:11:11"} {"ID":"13","Value":1,"date":"2023-06-30 11:11:11"} {"ID":"22","Value":1,"date":"2023-06-30 12:12:12"} {"ID":"33","Value":1,"date":"2023-06-30 21:21:21"} ```