Почему добавление круглых скобок к фильтру в » jq » создает допустимый JSON и без круглых скобок, несколько выходных данных объектов?

#json #command-line #jq

Вопрос:

С помощью jq я хотел бы задать свойство в данных JSON и позволить jq вывести исходный JSON с обновленным значением. Я нашел, более или менее методом проб и ошибок, решение и хочу понять, почему и как оно работает.

У меня есть следующие данные JSON:

 {
    "notifications": [
    {
      "source": "observer01",
      "channel": "error",
      "time": "2021-01-01 01:01:01"
    },
    {
      "source": "observer01",
      "channel": "info",
      "time": "2021-02-02 02:02:02"
    }
  ]
}
 

Моя цель состоит в том, чтобы обновить time свойство объекта с помощью определенного source и channel (исходный JSON намного длиннее с большим количеством объектов в notifications массиве того же формата).

(В следующем примере я хочу обновить time свойство observer01 with channel info , поэтому второй объект в приведенных выше данных примера.)

Моей первой попыткой, не приведшей к желаемому результату, была следующая jq команда:

 jq '.notifications[] | select(.source == "observer01" and .channel == "info").time = "NEWTIME"' data.json
 

Это приводит к следующему результату:

 {
  "source": "observer01",
  "channel": "error",
  "time": "2021-01-01 01:01:01"
},
{
  "source": "observer01",
  "channel": "info",
  "time": "NEWTIME"
}
 

Который представляет собой просто список объектов JSON в notifications массиве. Я понимаю, что это может быть полезно, например, для передачи объектов в другие инструменты командной строки.

Теперь давайте попробуем следующую jq команду, которая такая же, как и выше, плюс одна пара скобок:

 jq '(.notifications[] | select(.source == "observer01" and .channel == "info").time) = "NEWTIME"' data.json
 

Это приведет к желаемому результату, исходному действительному JSON с обновленным time свойством:

 {
    "notifications": [
    {
      "source": "observer01",
      "channel": "error",
      "time": "2021-01-01 01:01:01"
    },
    {
      "source": "observer01",
      "channel": "info",
      "time": "NEWTIME"
    }
  ]
}
 

Почему добавление круглых скобок в jq фильтр в приведенном выше случае приводит к другому результату?

Ответ №1:

Скобки просто меняют приоритет. Это задокументировано в man jq :

Скобки работают как оператор группировки, как и в любом типичном языке программирования.

       jq ´(.   2) * 5´
         1
      => 15
 

Давайте рассмотрим более простой пример:

 echo '[{"a":1}, {"a":2}]' | jq '.[] | .a |= . 1'
 

Это выводит

 {
  "a": 2
}
{
  "a": 3
}
 

потому что это интерпретируется как

                                       ↓         ↓
echo '[{"a":1}, {"a":2}]' | jq '.[] | (.a |= . 1)'
 

Первый фильтр .[] выводит элементы как отдельные объекты, затем они изменяются вторым фильтром.

Размещение скобок после первых двух элементов изменяет приоритет:

                                 ↓        ↓
echo '[{"a":1}, {"a":2}]' | jq '(.[] | .a) |= . 1'
 

и производит другой вывод:

 [
  {
    "a": 2
  },
  {
    "a": 3
  }
]
 

Кстати, это тот же вывод, что и из

 echo '[{"a":1}, {"a":2}]' | jq '.[].a |= . 1'
 

Он изменяет значение, связанное с "a" ключом в массиве.

Комментарии:

1. 1 Большое спасибо за ваше объяснение @choroba, ваши примеры помогли мне понять, какое значение имеют эти скобки.

Ответ №2:

Давайте сравним эти два.

  .notifications[] | select(...).time = "NEWTIME"

(.notifications[] | select(...).time) = "NEWTIME"
 

В первом фильтр верхнего уровня определяется | . Вход-это объект, а выход-результат применения select(...).time = "NEWTIME" к каждому полученному значению .notifications[] . По сути, исходный объект «потерян».

Во втором фильтр верхнего уровня определяется = . x = y возвращает свои входные данные в качестве выходных, но с побочным эффектом, создаваемым

  1. Определение того, к чему относится выражение пути x во входных данных,
  2. Оценка фильтра y на входе (даже такое выражение, как "NEWTIME" просто фильтр: то, которое игнорирует его ввод и возвращает строку "NEWTIME" )
  3. Присвоение результата действия y объекту, к которому обращается x .

Комментарии:

1. 1 Спасибо @chepner! Хотя @choroba также сделал отличный пост, я выбираю ваш в качестве принятого ответа, потому что вы дали несколько действительно полезных подробных объяснений.