问题
I want to modify the following json using variables specified in a Linux bash shell using jq.
var1="red"
var2="european.flowers"
var3="european_vegetables"
var4="20"
My json:
{
"plants": {
"flowers.small": {
"colour": "",
"age": "",
"vegetables": {
"root": "",
"height": ""
}
}
}
}
I want to modify the json using variables in jq:
{
"plants": {
"${var2}": {
"colour": "${var1}",
"age": "",
"${var3}": {
"root": "",
"height": "${var4}"
}
}
}
}
I am attempting to just set a field value from a variables:
Command:
cat myjson.json|jq '.plants["flowers.small"].colour = "${var1}"' -c
The result is:
{"plants":{"flowers.small":{"colour":"${var1}","age":"","vegetables":{"root":"","height":""}}}}
Command:
cat myjson.json|jq --arg v "$var1" '.plants.["flowers.small"].colour = [$v]' -c
The result is:
jq: error: syntax error, unexpected '[', expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1:
.plants.["flowers.small"].colour = $v
jq: 1 compile error
My jq version is: jq-1.5-1-a5b5cbe
How can I rename a field and set a value for the key from the variables? Is this even doable using the jq version?
回答1:
Using jq
as a template engine
If you don't really need input.json
to be a separate file, the easy thing to do is to define your entire template as a jq expression:
var1="red"
var2="european.flowers"
var3="european_vegetables"
var4="20"
jq -n --arg var1 "$var1" --arg var2 "$var2" --arg var3 "$var3" --arg var4 "$var4" '
{
"plants": {
"\($var2)": {
"colour": $var1,
"age": "",
"\($var3)": {
"root": "",
"height": $var4
}
}
}
}'
emits as output:
{
"plants": {
"european.flowers": {
"colour": "red",
"age": "",
"european_vegetables": {
"root": "",
"height": "20"
}
}
}
}
Alternately: Renaming subtrees
If you really do want to rename an existing key, consider an approach like the below. Using jq variables for the template fields isn't mandatory, but it does make it easier to change your code later:
var1="red"; var2="european.flowers"; var3="european_vegetables"; var4="20"
jq --arg var1 "$var1" --arg var2 "$var2" --arg var3 "$var3" --arg var4 "$var4" '
# set variables for the fields we expect to see in our input
"flowers.small" as $plant_tmpl |
"vegetables" as $cat_tmpl |
# change things inside fields we will later rename *before* we rename those fields
.plants[$plant_tmpl].colour = $var1 |
.plants[$plant_tmpl][$cat_tmpl].height = $var4 |
if $var3 == $cat_tmpl then . else
# var3 is not "vegetables" (templated value), so we need to rename it
.plants[$plant_tmpl][$var3] = .plants[$plant_tmpl][$cat_tmpl] |
del(.plants[$plant_tmpl][$cat_tmpl])
end |
if $var2 == $plant_tmpl then . else
.plants[$var2] = .plants[$plant_tmpl] |
del(.plants[$plant_tmpl])
end
' <<'EOF'
{
"plants": {
"flowers.small": {
"colour": "",
"age": "",
"vegetables": {
"root": "",
"height": ""
}
}
}
}
EOF
回答2:
I suspect this is very close to what you want:
#!/bin/bash
var1="red"
var2="european.flowers"
var3="european_vegetables"
var4="20"
jq --arg var1 "$var1" \
--arg var2 "$var2" \
--arg var3 "$var3" \
--arg var4 "$var4" '
{"${var1}": $var1, "${var2}": $var2, "${var3}": $var3, "${var4}": $var4} as $dict
| walk( if type == "string" and $dict[.] then $dict[.]
elif type=="object" then with_entries(if $dict[.key] then .key |= $dict[.] else . end)
else . end)' template.json
This general approach is OK, but you might want to check the jq Cookbook for suggestions about using jq as a template engine: https://github.com/stedolan/jq/wiki/Cookbook#using-jq-as-a-template-engine
Streamlined version
If your jq supports $ARGS
, then you could use this streamlined version of the above:
jq --arg '${var1}' "$var1" \
--arg '${var2}' "$var2" \
--arg '${var3}' "$var3" \
--arg '${var4}' "$var4" '
$ARGS.named as $dict
| walk( if type == "string" and $dict[.] then $dict[.]
elif type=="object" then with_entries(if $dict[.key] then .key |= $dict[.] else . end)
else . end)' template.json
来源:https://stackoverflow.com/questions/54601240/using-jq-to-change-json-object-and-field-value-from-variable