如何在基于字段值替换项目的同时在 jq 中输出整个文档?

Sea*_*een 4 json edit amazon-cloudfront aws-cli jq

我正在尝试使用 jq 来解析 AWS CloudFront 配置 JSON 文件并更改一些值,以便我可以使用该配置发出更新语句。

文件

具有编辑值的重要文档格式片段是:

{
    "ETag": "REDACTED",
    "DistributionConfig": {
        "Origins": {
            "Quantity": 2,
            "Items": [
                {
                    "Id": "redacted-1",
                    "DomainName": "redacted1.us-east-1.amazonaws.com",
                    "OriginPath": "/redacted"
                },
                {
                    "Id": "redacted-2",
                    "DomainName": "redacted2.s3.amazonaws.com",
                    "OriginPath": ""
                }
            ]
        }
    }    
}
Run Code Online (Sandbox Code Playgroud)

我想做什么

我想输出整个文档,但是:

  • 我想将ETag值设置为""
  • 在 items 数组中的第二个项目上,我想将 设置OriginPath为我选择的值

(这是为了支持我们的 CI/CD 流程能够将 CloudFront 分发指向 S3 存储桶中刚刚部署的代码的新文件夹。我想以这些特定方式修改现有配置,但保持其余部分不变。 )

有用的东西……有点

一个 jq 过滤器. | (.DistributionConfig.Origins.Items[1].OriginPath = "Hello") | .ETag = ""做我需要它做的事情,结果是:

{
  "ETag": "", // correctly updated
  "DistributionConfig": {
    "Origins": {
      "Quantity": 2, // correctly retained
      "Items": [
        {
          "Id": "redacted-1",
          "DomainName": "redacted1.us-east-1.amazonaws.com",
          "OriginPath": "/redacted"
        },
        {
          "Id": "redacted-2",
          "DomainName": "redacted2.s3.amazonaws.com",
          "OriginPath": "Hello" // correctly updated
        }
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我遇到问题的地方

上面的解决方案有效......只要我指的是数组中的第二项。但我并不总是确定它会是数组中的第二项。

所以相反,我想根据Id属性进行匹配。

我能够找到的任何解决方案似乎都是将文档过滤到 JSON 的那部分,而不是匹配和更新值作为输出文档的一部分。

如何过滤具有特定值的字段的数组,并仍然输出整个文档?

或者换一种方式 - 鉴于上面的文件,我该如何更改:

. | (.DistributionConfig.Origins.Items[1].OriginPath = "Hello") | .ETag = ""
Run Code Online (Sandbox Code Playgroud)

是让我参考.Id="redaacted-2"而不是Items[1]?

演示链接

https://jqplay.org/s/ZQ7XcM5-BY上的脚本演示,以防有人想尝试答案。

pea*_*eak 6

完成此类编辑的基本方法是使用以下语法:

PATHSPEC = VALUE
Run Code Online (Sandbox Code Playgroud)

或者如果该值以某种方式取决于 PATHSPEC:

PATHSPEC |= VALUE
Run Code Online (Sandbox Code Playgroud)

其中 PATHSPEC 是 jq 路径规范。

使用这个原则,在你的情况下,你可以写:

.ETag = ""
| .DistributionConfig.Origins.Items[1].OriginPath = "myvalue"
Run Code Online (Sandbox Code Playgroud)

或者,如果您想基于 .Id 进行第二次更新:

.ETag = ""
| .DistributionConfig.Origins.Items[] |=
   if .Id == "redacted-2" then .OriginPath = "myvalue" else . end
Run Code Online (Sandbox Code Playgroud)

上面的例子可以在https://jqplay.org/s/u5xbhbSs4l 上看到

变化

当然有许多变化。例如,您可以使用 .Quantity 作为要更新的项目的索引:

.ETag = ""
| .DistributionConfig.Origins.Quantity as $ix
| .DistributionConfig.Origins.Items[$ix - 1].OriginPath = "myvalue"
Run Code Online (Sandbox Code Playgroud)

或更干:

.ETag = ""
| .DistributionConfig.Origins |=
    (.Items[.Quantity  - 1].OriginPath = "myvalue")

Run Code Online (Sandbox Code Playgroud)


che*_*ner 5

只是 @peak 的答案的一个微小变化,用于select选择要更新的项目:

.ETag = ""
| (.DistributionConfig.Origins.Items[] | 
      select(.Id == "redacted-2")
  ).OriginPath = "foo"
Run Code Online (Sandbox Code Playgroud)

第二个赋值接收整个原始输入作为其输入,因此将其生成作为其输出。不过,带括号的过滤器仅选择要更新Items其键的适当元素。OriginPath