首页 > 解决方案 > jq- 循环中的参考当前位置

问题描述

我有一个名为的日志文件log.json,其格式如下:

{"msg": "Service starting up!"}  
{"msg": "Running a job!"}
{"msg": "Error detected!"}

还有另一个名为 的文件messages.json,如下所示:

{"msg": "Service starting up!", "out": "The service has started"}
{"msg": "Error detected!", "out": "Uh oh, there was an error!"}
{"msg": "Service stopped", "out": "The service has stopped"}

我正在尝试使用jq读取两个文件的函数编写一个函数,并且每当它找到与 a msginlog.json匹配的 a msgin 时messages.json,打印outin 相应行中的值messages.json。因此,在这种情况下,我希望将其作为输出:

"The service has started"
"Uh oh, there was an error!"

到目前为止,我能够得到的最接近的是以下内容:

jq --argfile a log.json --argfile b messages.json -n 'if ($a[].msg == $b[].msg) then $b[].out else empty end'

这成功地执行了我希望进行的所有比较。但是,out它不是打印我正在寻找的特定内容,而是out在 if 语句返回 true 时打印 every (这是有道理的。$b[].out从未重新定义,并要求它们中的每一个)。因此,此语句输出:

"The service has started"
"Uh oh, there was an error!"
"The service has stopped"
"The service has started"
"Uh oh, there was an error!"
"The service has stopped"

所以在这一点上,我需要一些方法来请求$b[current_index].out,然后打印出来。有没有办法让我做到这一点(或者我可以使用完全独立的方法)?

标签: jsondictionaryjq

解决方案


messages.json 有效地定义了一个字典,所以让我们从创建一个我们可以轻松查找的 JSON 字典开始。这可以使用 INDEX/2 方便地完成,它(如果你的 jq 没有它)定义如下:

def INDEX(stream; idx_expr):
  reduce stream as $row ({};
    .[$row|idx_expr|
      if type != "string" then tojson
      else .
      end] |= $row);

第一个解决方案现在很简单:

INDEX($messages[]; .msg) as $dict
| inputs
| $dict[.msg]
| .out 

假设这是在 program.jq 中,适当的调用如下(特别注意-n选项):

jq -n --slurpfile messages messages.json -f program.jq log.json

null如果.msg日志文件中的 不在字典中,则将打印上述内容。要过滤掉这些空值,您可以(例如)添加select(.)到管道中。

另一种可能性是使用原始的.msg,如在此变体中:

INDEX($messages[]; .msg) as $dict
| inputs
| . as $in
| $dict[.msg]
| .out // $in.msg

推荐阅读