在根据字段值替换项目时,如何在 jq 中输出整个文档?
How can I output the whole document in jq while replacing an item based on a field's value?
我正在尝试使用 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": ""
}
]
}
}
}
我想做什么
我想输出整个文档,但是:
- 我想将
ETag
值设置为 ""
- 在项目数组的第 2 项上,我想将
OriginPath
设置为我选择的值
(这是为了支持我们的 CI/CD 进程能够将 CloudFront 分发指向 S3 存储桶中刚刚部署的代码的新文件夹。我想以这些特定方式修改现有配置,但是保留其余部分。)
有用的东西……有点像
. | (.DistributionConfig.Origins.Items[1].OriginPath = "Hello") | .ETag = ""
的 jq 过滤器做了我需要它做的事情,导致:
{
"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
}
]
}
}
}
哪里有问题
上面的解决方案有效...只要我指的是数组中的第 2 项。但我并不总是确定它 会是 数组中的第二个项目。
因此,我想根据 Id
属性.
进行匹配
我找到的任何执行此操作的解决方案似乎都将文档过滤到 JSON 的那个部分,而不是将匹配和更新值作为输出文档的一部分。
问题
如何为具有特定值的字段过滤数组,并仍然输出整个文档?
或者换句话说——给定上面的文档,我该如何更改:
. | (.DistributionConfig.Origins.Items[1].OriginPath = "Hello") | .ETag = ""
让我参考 .Id="redaacted-2"
而不是 Items[1]
的东西?
A link 演示
https://jqplay.org/s/ZQ7XcM5-BY 处的脚本演示,以防有人想尝试回答。
完成此类编辑的基本方法是使用语法:
PATHSPEC = VALUE
或者如果该值以某种方式取决于 PATHSPEC:
PATHSPEC |= VALUE
其中 PATHSPEC 是 jq 路径规范。
使用这个原则,在你的情况下,你可以写:
.ETag = ""
| .DistributionConfig.Origins.Items[1].OriginPath = "myvalue"
或者,如果您希望第二次更新基于 .Id:
.ETag = ""
| .DistributionConfig.Origins.Items[] |=
if .Id == "redacted-2" then .OriginPath = "myvalue" else . end
上面的示例可以在 https://jqplay.org/s/u5xbhbSs4l
上看到。
变化
当然有很多变化。例如,您可以使用 .Quantity 作为要更新的项目的索引:
.ETag = ""
| .DistributionConfig.Origins.Quantity as $ix
| .DistributionConfig.Origins.Items[$ix - 1].OriginPath = "myvalue"
或更多 DRYly:
.ETag = ""
| .DistributionConfig.Origins |=
(.Items[.Quantity - 1].OriginPath = "myvalue")
只是@peak 回答的一个小变化,使用select
选择要更新的项目:
.ETag = ""
| (.DistributionConfig.Origins.Items[] |
select(.Id == "redacted-2")
).OriginPath = "foo"
第二个赋值将整个原始输入作为输入,因此将其作为输出。但是,带括号的过滤器仅选择 Items
中您要更新其 OriginPath
键的适当元素。
我正在尝试使用 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": ""
}
]
}
}
}
我想做什么
我想输出整个文档,但是:
- 我想将
ETag
值设置为""
- 在项目数组的第 2 项上,我想将
OriginPath
设置为我选择的值
(这是为了支持我们的 CI/CD 进程能够将 CloudFront 分发指向 S3 存储桶中刚刚部署的代码的新文件夹。我想以这些特定方式修改现有配置,但是保留其余部分。)
有用的东西……有点像
. | (.DistributionConfig.Origins.Items[1].OriginPath = "Hello") | .ETag = ""
的 jq 过滤器做了我需要它做的事情,导致:
{
"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
}
]
}
}
}
哪里有问题
上面的解决方案有效...只要我指的是数组中的第 2 项。但我并不总是确定它 会是 数组中的第二个项目。
因此,我想根据 Id
属性.
我找到的任何执行此操作的解决方案似乎都将文档过滤到 JSON 的那个部分,而不是将匹配和更新值作为输出文档的一部分。
问题
如何为具有特定值的字段过滤数组,并仍然输出整个文档?
或者换句话说——给定上面的文档,我该如何更改:
. | (.DistributionConfig.Origins.Items[1].OriginPath = "Hello") | .ETag = ""
让我参考 .Id="redaacted-2"
而不是 Items[1]
的东西?
A link 演示
https://jqplay.org/s/ZQ7XcM5-BY 处的脚本演示,以防有人想尝试回答。
完成此类编辑的基本方法是使用语法:
PATHSPEC = VALUE
或者如果该值以某种方式取决于 PATHSPEC:
PATHSPEC |= VALUE
其中 PATHSPEC 是 jq 路径规范。
使用这个原则,在你的情况下,你可以写:
.ETag = ""
| .DistributionConfig.Origins.Items[1].OriginPath = "myvalue"
或者,如果您希望第二次更新基于 .Id:
.ETag = ""
| .DistributionConfig.Origins.Items[] |=
if .Id == "redacted-2" then .OriginPath = "myvalue" else . end
上面的示例可以在 https://jqplay.org/s/u5xbhbSs4l
上看到。变化
当然有很多变化。例如,您可以使用 .Quantity 作为要更新的项目的索引:
.ETag = ""
| .DistributionConfig.Origins.Quantity as $ix
| .DistributionConfig.Origins.Items[$ix - 1].OriginPath = "myvalue"
或更多 DRYly:
.ETag = ""
| .DistributionConfig.Origins |=
(.Items[.Quantity - 1].OriginPath = "myvalue")
只是@peak 回答的一个小变化,使用select
选择要更新的项目:
.ETag = ""
| (.DistributionConfig.Origins.Items[] |
select(.Id == "redacted-2")
).OriginPath = "foo"
第二个赋值将整个原始输入作为输入,因此将其作为输出。但是,带括号的过滤器仅选择 Items
中您要更新其 OriginPath
键的适当元素。