为什么在 'jq' 中向过滤器添加括号会产生有效的 JSON 而没有括号会产生多个对象输出?

Why is adding parentheses to a filter in 'jq' producing valid JSON and without parentheses, multiple outputs of objects?

使用 jq, I would like to set a property within JSON data and let 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"
    }
  ]
}

我的目标是用特定的 sourcechannel 更新对象的 time 属性(原来的 JSON 更长notifications 数组中有很多相同格式的对象)。

(在下面的例子中,我想用通道info更新observer01time属性,所以上面例子数据中的第二个对象。 )

我的第一次尝试是以下 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"
}

这只是 notifications 数组中 JSON 个对象的列表。我知道这很有用,例如将对象通过管道传输到其他命令行工具。

现在让我们试试下面的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 过滤器会产生不同的输出?

括号只是改变了优先级。它记录在 man jq:

Parenthesis work as a grouping operator just as in any typical programming language.

      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
  }
]

顺便说一句,这与 from

的输出相同
echo '[{"a":1}, {"a":2}]' | jq '.[].a |= .+1'

它更改与数组中的 "a" 键关联的值。

让我们比较一下两者。

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

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

在第一个中,顶级过滤器由 | 定义。输入是一个对象,输出是将 select(...).time = "NEWTIME" 应用于 .notifications[] 产生的每个值的结果。本质上,原来的对象“丢失”了。

在第二个中,顶级过滤器由 = 定义。 x = y returns 其输入作为输出,但具有

产生的副作用
  1. 确定路径表达式 x在输入中指的是什么,
  2. 在输入上评估 过滤器 y,(即使像 "NEWTIME" 这样的表达式也只是一个过滤器:忽略其输入和 returns 字符串 "NEWTIME")
  3. 正在将 y 的结果分配给 x 所处理的事物。