I've asked a similar question in the past but I'm having a hard time adapting that solution to this problem.
I have the following json array:
$ jq <<<"$json"
[
{
"id": "node1"
},
{
"id": "node2"
},
{
"id": "node3"
}
]
I want to add a key/value to each node or modify it if it already exists. I can sort of do that with:
$ jq '.[] | select(.id == "node2") += {status: "fail"}' <<<"$json"
{
"id": "node1"
}
{
"id": "node2",
"status": "fail"
}
{
"id": "node3"
}
But notice the outer array disappears so when I try to implement this solution into my script it fails.
#!/usr/bin/env bash
[[ $DEBUG == true ]] && set -x
nodes=(node1 node2 node3)
json='[{"id": "node1"},{"id": "node2"},{"id": "node3"}]'
for node in "${nodes[@]}"; do
if [[ $node == node2 ]]; then
status=fail
else
status=pass
fi
json=$(jq --arg status "$status" --arg node "$node" '.[] | select(.id == $node) += {status: $status}' <<<"$json")
done
Error:
$ ./script.sh
jq: error (at <stdin>:4): Cannot index string with string "id"
jq: error (at <stdin>:7): Cannot index string with string "id"
jq: error (at <stdin>:10): Cannot index string with string "id"
Is there a way I can modify these json objects while retaining the overall structure?
Best Answer
Once you have picked out one of the elements from
.[]
withselect()
, that's the only data you're left with and what will be outputted at the end.Instead, use
map()
to apply a (possible) modification to each of the elements of the top-level array:Or possibly neater, with fewer braces,
Replicating your shell loop in
jq
to avoid multiple invocations ofjq
:Or, failing multiple nodes with each failed node listed on the command line (note the unorthodox but necessary placing of
--args
at the end):