如何使用 jq 查找和替换多个字段值?

use*_*698 13 sed json jq

在以下 json 文件中,

{
  "email": "xxx",
  "pass": "yyy",
  "contact": [
    {
      "id": 111,
      "name": "AAA"
    }
  ],
  "lname": "YYY",
  "name": "AAA",
   "group": [
    {
      "name": "AAA",
      "lname": "YYY",
    }
  ],
Run Code Online (Sandbox Code Playgroud)

我需要在所有位置查找键“名称”并将其值替换为“XXX”。哪个 jq 命令可以做到这一点?

Mic*_*mer 13

jq 的赋值操作可以一次在尽可能多的位置上执行更新,并且是为这种情况而设计的。您可以使用

jq '(.. | .name?) |= "XXXX"'
Run Code Online (Sandbox Code Playgroud)

任何地方找到每个名为“name”的字段,并将每个字段中的值一次性替换为“XXXX”,并输出结果对象。

这只是递归下降文档中结合更新分配..|.a?示例。

它使用递归下降运营商..找到树中的每个单独的值,然后从他们每个人翻出了“名称”字段中.name抑制非匹配值的任何错误用?,然后在一次更新对象在所有这些地方使用更新赋值运算符|=使用“XXXX” ,并输出新对象。

无论文件结构如何,这都将起作用,并在任何地方更新每个名称字段。


或者,如果文件始终具有此结构,并且您想要更改是那些特定的“名称”字段,而不仅仅是任何旧名称,您也可以将它们列出并作为一个组分配给它们:

jq '(.name, .contact[].name, .group[].name) |= "XXXX"'
Run Code Online (Sandbox Code Playgroud)

这对

  1. 顶级对象的“名称”字段;
  2. “contact”数组中每个对象的“name”字段;和
  3. “组”数组中每个对象的“名称”字段。

一口气完成。如果文件中可能有其他您不想更改的不相关名称字段,这将特别有用。它只找到在那里命名的三组位置并同时更新它们。


如果值只是像这里这样的文字,那么简单的赋值也可以=工作并为您节省一个字符:(..|.name?)="XXXX"- 如果您的值是基于整个顶级对象计算的,您也需要这个。相反,如果您想根据旧名称计算新名称,则需要使用|=. 如果我不确定要使用什么,|=通常在极端情况下会有更好的行为。

如果您有多个替换要做,您可以将它们组合在一起:

jq '(..|.name?) = "XXXX" | (..|.lname?) = "1234"'
Run Code Online (Sandbox Code Playgroud)

将在任何地方更新“name”和“lname”字段,并输出整个更新的对象一次。


其他一些可能有效的方法:

它查找所有内容,然后是对象,然后是具有“名称”的对象,然后是这些对象上的名称字段,并执行与以前相同的更新。

  • 如果你运行的JQ(不太可能)的开发版本,那么该walk功能还可以做的工作:walk(.name?="XXXX")。所有其他版本都适用于最新发布的版本 1.5。

  • 另一种多更新可能是

      jq '(..|select(has("name"))?) += {name: "XXXX", lname: "1234"}'
    
    Run Code Online (Sandbox Code Playgroud)

它找到具有名称的所有内容然后使用算术更新赋值*=对象合并行为+在每个对象上设置“name”和“lname” 。

  • `jq '(.. | .name?) |= "XXXX"'` 具有引入先前为空的值“name”的预期效果,我只想替换存在的值。 (2认同)

oli*_*liv 10

使用jq基于walk函数(需要最新版本):

jq 'walk(.name?="XXX")' file
Run Code Online (Sandbox Code Playgroud)

如果您jq不支持该walk功能,只需将其定义为:

jq '
  # Apply f to composite entities recursively, and to atoms
  def walk(f):
    . as $in
    | if type == "object" then
       reduce keys[] as $key
         ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
    elif type == "array" then map( walk(f) ) | f
    else f
    end;
  walk(.name?="XXX")
' file
Run Code Online (Sandbox Code Playgroud)

学分:https : //github.com/stedolan/jq/issues/963