To be assigned value as variable in jq [duplicate] - json

This question already has answers here:
Passing bash variable to jq
(10 answers)
Closed 5 years ago.
json processor jq for a package.json
{
"someparent" : {
"somechild" : "oldvalue"
}
}
If I run the following command (to change oldvalue to somevalue):
jq '.someparent["somechild"] = "somevalue" "$aDirection/package.json"'
It is successfully changed. However, if I give a variable instead of someValue:
aVar="anotherValue"
jq '.someparent["somechild"] = "$aVar" "$aDirection/package.json"'
It is not working.
What I already tried:
["aVar"] #interpreted as string
["$aVar"] #interpreted as string
"$aVar" # aVar is not defined
$avar #aVar is not defined

The single quotes around the jq filter are in the wrong place. In your attempt made it is also enclosing the actual JSON file input. The right syntax when applying filter over files is
jq '<filter>' json-file
In your case the right filter is just
.someparent["somechild"] = $aVar
and to use a shell variable in to the jq, you need to use the --arg field. Putting together both of the options. The first variable after --arg is used in the context of jq and the one following it is the actual shell variable.
aVar="anotherValue"
jq --arg aVar "$aVar" '.someparent["somechild"] = $aVar' "$aDirection/package.json"
# ^^^^ Remember the single quote
# terminating here and not extended

Just to be clear: using the OP's approach can be made to work, e.g.
jq '.someparent["somechild"] = "'"$aVar"'"' "$aDirection/package.json"
but using --arg (for strings) and/or --argjson is far better. An additional option for environment variables is to use the jq function env.

Related

How to replace values in a JSON dictionary with their respective shell variables in jq?

I have the following JSON structure:
{
"host1": "$PROJECT1",
"host2": "$PROJECT2",
"host3" : "xyz",
"host4" : "$PROJECT4"
}
And the following environment variables in the shell:
PROJECT1="randomtext1"
PROJECT2="randomtext2"
PROJECT4="randomtext3"
I want to check the values for each key, if they have a "$" character in them, replace them with their respective environment variable(which is already present in the shell) so that my JSON template is rendered with the correct environment variables.
I can use the --args option of jq but there are quite a lot of variables in my actual JSON template that I want to render.
I have been trying the following:
jq 'with_entries(.values as v | env.$v)
Basically making each value as a variable, then updating its value with the variable from the env object but seems like I am missing out on some understanding. Is there a straightforward way of doing this?
EDIT
Thanks to the answers on this question, I was able to achieve my larger goal for a part of which this question was asked
iterating over each value in an object,
checking its value,
if it's a string and starts with the character "$"
use the value to update it with an environment variable of the same name .
if it's an array
use the value to retrieve an environment variable of the same name
split the string with "," as delimiter, which returns an array of strings
Update the value with the array of strings
jq 'with_entries(.value |= (if (type=="array") then (env[.[0][1:]] | split(",")) elif (type=="string" and startswith("$")) then (env[.[1:]]) else . end))'
You need to export the Bash variables to be seen by jq:
export PROJECT1="randomtext1"
export PROJECT2="randomtext2"
export PROJECT4="randomtext3"
Then you can go with:
jq -n 'with_entries((.value | select(startswith("$"))) |= env[.[1:]])'
and get:
{
"host1": "randomtext1",
"host2": "randomtext2",
"host3": "xyz",
"host4": "randomtext3"
}
Exporting a large number of shell variables might not be such a good idea and does not address the problem of array-valued variables. It might therefore be a good idea to think along the lines of printing the variable=value details to a file, and then combining that file with the template. It’s easy to do and examples on the internet abound and probably here on SO as well. You could, for example, use printf like so:
printf "%s\t" ${BASH_VERSINFO[#]}
3 2 57 1
You might also find declare -p helpful.
See also https://github.com/stedolan/jq/wiki/Cookbook#arbitrary-strings-as-template-variables

Shell Script jq reading a json key with period (.) inside a variable [duplicate]

This question already has answers here:
Passing bash variable to jq
(10 answers)
"Invalid numeric literal" error from jq trying to modify JSON with variable
(1 answer)
Closed 4 years ago.
Here is the example json
{
"app": "K8s",
"version": "1.8",
"date": "2018-10-10"
}
In order to get the value of app, I can do this in jq as
jq '.app'
But what I want is, I want to pass the key to jq as a bash variable, i.e
bash_var="app"
jq '."${bash_var}"'
I'm getting the output as null instead of the value. What is the correct syntax to achieve this?
First, you need to port the bash variable into jq's context usign the --arg flag and access it inside the [..]
jq --arg keyvar "$bash_var" '.[$keyvar]' json

Why bash does not json split array with jq -r bash IFS explicitly defined variable [duplicate]

This question already has answers here:
Convert a JSON array to a bash array of strings
(4 answers)
Closed 2 years ago.
I am reading an array from a file
file.json
{
"content": ["string with spaces", "another string with spaces", "yet another string with spaces"]
}
#!/bin/bash
GROUP_ID_TEMP=( $(IFS=','; jq -r '.content' file.json) )
why does jq read content print or echo content as space separated array for the below codeblock at the whitespace rather than the comma ',' as explicitly stated?
for each in "${GROUP_ID_TEMP[#]}"
do
echo "$each" >> "file.txt"
done
Here's an easier way to reproduce your problem:
var=( $(IFS=','; echo "foo,bar" ) )
declare -p var
What you expect is declare -a var=([0]="foo" [1]="bar") but what you get is declare -a var=([0]="foo,bar")
This happens because IFS affects word splitting and not program output. Since there is no word splitting in the scope of your variable, it doesn't affect anything.
If you instead define it in the scope that does word splitting, i.e. the scope in which the $(..) expansion happens:
IFS=','
var=( $(echo "foo,bar") )
then you get the result you expect.

How to add bash variable to json file? [duplicate]

This question already has answers here:
Modify a key-value in a json using jq in-place
(8 answers)
Closed 1 year ago.
I have a daemon.json file which contains one line as below
{ "insecure-registries":["192.X.X.X:8123"] }
I am trying to use a variable to change generically to the current IP address. In bash script normally
I'd store in a variable like
myip=hostname -I | awk '{print $1}'
{ "insecure-registries":["$myip:8123"] }
How to use a kind of variable in JSON file?
If you have access to jq, I would recommend storing not JSON, but a jq filter, like
{"insecure-registries": ["\($ip):8123"]}
Assume the preceding is in file named foo.jq; then using jq as follows to produce JSON from the filter.
$ myip=$(hostname -I | awk '{print $1}') # 192.0.2.42, e.g.
$ jq -nf foo.jq --arg ip "$myip"
{
"insecure-registries": [
"192.0.2.42:8123"
]
}
JSON itself doesn't have a notion of substitution, and bash itself isn't really suitable for making substitutions like this.

Conditional variables in JQ json depending on argument value?

I am trying to build a json with jq with --arg arguments however I'd like for the json not to be able to have a condition if the variable is empty.
An example, if I run the following command
jq -n --arg myvar "${SOMEVAR}" '{ $myvar}'
I'd like the json in that case to be {} if myvar happens to be empty (Because the variable ${SOMEVAR} does not exist) and not { "myvar": "" } which is what I get by just running the command above.
Is there any way to achieve this through some sort of condition?
UPDATE:
Some more details about the use case
I want to build a json based on several environment variables but only include the variables that have a value.
Something like
{"varA": "value", "varB": "value"}
But only include varA if its value is defined and so on. The issue now is that if value is not defined, the property varA will still exist with an empty value and because of the multiple argument/variable nature, using an if/else to build the entire json as suggested will lead to a huge amount of conditions to cover for every possible combination of variables not existing
Suppose you have a template of variable names, in the form of an object as you have suggested you want:
{a, b, c}
Suppose also (for the sake of illustration) that you want to pull in the corresponding values from *ix environment variables. Then you just need to adjust the template, which can be done using this filter:
def adjust: with_entries( env[.key] as $v | select($v != null) | .value = $v );
Example:
Assuming the above filter, together with the following line, is in a file named adjust.jq:
{a,b,c} | adjust
then:
$ export a=123
$ jq -n -f -c adjust.jq
{"a":"123"}
You can use an if/else construct:
jq -n --arg myvar "${SOMEVAR}" 'if ($myvar|length > 0) then {$myvar} else {} end'
It's still not clear where the variable-value pairs are coming from, so maybe it would be simplest to construct the object containing the mapping before invoking jq, and then passing it in using the --argjson or --argfile option?