将jq查询的结果汇总到单个数组中

时间:2019-07-05 22:10:24

标签: jq

我有一个具有独立JSON对象(即不是数组)的输入文件,我想从每个对象中过滤几个字段,并使用结果元素创建一个数组。它基本上是JSON格式的日志语句列表。

为此,我正在使用jq,它工作得很好,除了不能将所有结果对象聚合到单个数组中。

输入内容如下:

{"name":"myname", "environment":"staging", "email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"}
{"name":"myname", "environment":"staging", "email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"}
...
{"name":"myname", "environment":"staging", "email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}
{"name":"myothername", "environment":"staging", "time":"2017-10-02T05:00:00.046Z"}

(请注意,最后一项没有email字段,因此如果未过滤,它将返回null值)

从该对象列表中,我只想获取字段emailtime,而忽略其余字段,因此我使用以下jq查询:

jq '{email: (.email | values), time: (.time | values)}' input.json

请注意,由于日志消息是混合的,因此我使用values过滤器,因此并非所有json对象都具有email字段,因此我将其忽略。

我的问题是,即使我得到了期望的结果,我仍然得到一个列表,并且我想要一个数组。

即我得到类似

{"email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"}
{"email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"}
...
{"email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}

我想要它:

[
    {"email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"},
    {"email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"},
    ...,
    {"email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}
]

我尝试了几种不同的方法,但是通常会遇到错误Cannot index array with string "email",该错误告诉我数组操作出错了。

我尝试将查询包装在map()中,即map({.userEmail, .time}),尝试使用-s对数据进行筛选,并尝试使用|+|=运算符。

我也尝试过将查询包装在[{email: (.email|values), time:.time }]之类的数组括号内,但是得到的对象却相同,只是每个对象本身都包装在数组中,即

[{"email":"email1@example.com", "time":"2017-04-02T05:00:00.046Z"}]
[{"email":"email2@example.com", "time":"2017-02-02T05:00:00.046Z"}]
...
[{"email":"email3@example.com", "time":"2017-10-02T05:00:00.046Z"}]

这似乎很容易做,或者至少是一个普通的操作,但是我找不到正确的查询。

当输入不是数组时,将查询结果聚合到数组中的正确方法是什么?

3 个答案:

答案 0 :(得分:1)

更好...

  1. 根据示例数据,您的基本过滤器可以简化为{email, time}

  2. 通常,最好避免“模糊处理”输入(例如,以节省内存)。在您的情况下,可以通过将inputs与-n命令行选项一起使用来实现。

将它们放在一起:

jq -n '[inputs | {email, time }]' input.json

如果要过滤掉某些输入,则可以使用select,例如

jq -n '[inputs | select(.email) | {email, time } ]' input.json

答案 1 :(得分:0)

阅读更多内容后,我发现了想要的结果,它是slurp运算符和map的组合。

我意识到查询

jq -s 'map({email: (.email|values), time:.time })' input.json

将所有输入项读取为数组,然后读取为per the definition of map()

  

对于任何过滤器x,map(x)将对输入数组的每个元素运行该过滤器,并将输出返回到新数组中

所以两者结合起来就给了我所需的结果。

答案 2 :(得分:0)

对于那些喜欢替代(即非jq)解决方案的人,以下是一个基于JSON的步行路径unix工具的解决方案: jtc

  • 假设输入文件为inputs.json
    bash $ jtc -w'[email]<e>v[-1][time]' -T'{"email":{{e}},"time":{{}}}' -J inputs.json
    [
       {
          "email": "email1@example.com",
          "time": "2017-04-02T05:00:00.046Z"
       },
       {
          "email": "email2@example.com",
          "time": "2017-02-02T05:00:00.046Z"
       },
       {
          "email": "email3@example.com",
          "time": "2017-10-02T05:00:00.046Z"
       }
    ]
    bash $ 

这里的步行路径(-w)很简单:首先用email标记地址,然后将值存储到命名空间e中,然后在JSON中上一级,并用{寻址{1}}条记录。

然后让输出经过模板迭代(time),对存储的值-T和最后一个遍历的{{e}}进行插值,该时间恰好是时间戳。

{{}}将所有遍历收集到JSON数组中

PS>披露:我是-J工具的创建者