I have a json like this
[{"version": 0.0,"Resources": [ {"TargetService": {"Type": "AWS::ECS::Service","Properties": {"TaskDefinition": "abc","LoadBalancerInfo": {"ContainerName": "def","ContainerPort": 8080}}}} ]}]
My attempt is to replace TaskDefinition key value from "abc" to "123" and ContainerName key value from "def" to "456 in one command and return the whole update json.
This is what i tried
echo $APP_SPEC | jq --arg TASK_DEFINITION "123" '(.[].Resources[].TargetService | select(.Properties)).TaskDefinition |=$TASK_DEFINITION')
But the substistuion is not happening properly and the value gets appended at the end of the josn as below.
Incorrect response:
[ { "version": 0, "Resources": [ { "TargetService": { "Type": "AWS::ECS::Service", "Properties": { "TaskDefinition": "abc", "LoadBalancerInfo": { "ContainerName": "container_name", "ContainerPort": 8080 } }, "TaskDefinition": "123" } } ] } ]
CodePudding user response:
You don't need select
, you can specify the path directly:
.[].Resources[].TargetService.Properties.TaskDefinition = "123"
| .[].Resources[].TargetService.Properties.LoadBalancerInfo.ContainerName = "456"
Or group by the top level property:
.[].Resources[].TargetService.Properties |= (
.TaskDefinition |= "123"
| .LoadBalancerInfo.ContainerName |= "456"
)
It's also possible to recursively merge the expected object into your existing object using *=
:
.[].Resources[].TargetService.Properties *= {
TaskDefinition: "123",
LoadBalancerInfo: {
ContainerName: "456"
}
}
Output of all three variants:
[
{
"version": 0,
"Resources": [
{
"TargetService": {
"Type": "AWS::ECS::Service",
"Properties": {
"TaskDefinition": "123",
"LoadBalancerInfo": {
"ContainerName": "def",
"ContainerPort": 456
}
}
}
}
]
}
]
CodePudding user response:
Use the update operator |=
.[].Resources[].TargetService.Properties |= (
.TaskDefinition = "123"
| .LoadBalancerInfo.ContainerName = "456"
)
Note that select
only filters its input, it does not descend into the filter criterion. So, if you only want to make the update if the .Properties
field exists, use select
and descend into it.
( .[].Resources[].TargetService
| select(.Properties).Properties
) |= (
.TaskDefinition = "123"
| .LoadBalancerInfo.ContainerName = "456"
)
Note that the filter select(.Properties)
will produce false
if the content of that field, albeit existing, evaluates to null
or false
. If you want to consider such cases also as "existent", use has
in the select
filter to test for it.
( .[].Resources[].TargetService
| select(has("Properties")).Properties
) |= (
.TaskDefinition = "123"
| .LoadBalancerInfo.ContainerName = "456"
)
CodePudding user response:
Both the above solutions works perfectly fine. Thanks @Knittl and @pmf. For others who are stuck with this kind of problem here is my complete command to pass two variables to jq and replace the values in one shot.
echo $APP_SPEC | jq --arg TASK_DEFINITION "$NEW_TASK_DEFINITION_ARN" --arg CONTAINER_NAME "$CONTAINER_NAME" '.[].Resources[].TargetService.Properties |= (.TaskDefinition = $TASK_DEFINITION | .LoadBalancerInfo.ContainerName = $CONTAINER_NAME )'