jq if value contains then append - json

This might not be the ideal way to approach this but I am working on bulk update of some Grafana dashboards. When the expr key contains value that includes something like "sum((rate" I want to append to the value another string. Is this even possible?
if (.dashboard.panels[].targets[].expr | contains("sum((rate")) then . += "TEST" end'
I've tried a few variations of then action and even removed the concatanation to see if I could get simple replace. But getting
jq: error: syntax error, unexpected end (Unix shell quoting issues?) at <top-level>, line 1:

You should be able to update-assign part of your objects:
.dashboard.panels[].targets[].expr |= if contains("sum((rate") then . + "TEST" else . end
It's also possible to do without the if conditional, by first selecting all the interesting paths and then modifying only them:
(.dashboard.panels[].targets[].expr | select(contains("sum((rate"))) += "TEST"

You always need an else branch. For readability you can also pull the target up front (otherwise . will match the previous context).
.dashboard.panels[].targets[].expr |=
if contains("sum((rate") then . + "TEST" else . end
Demo
.dashboard.panels[].targets[].expr |= . +
if contains("sum((rate") then "TEST" else "" end
Demo

Related

Use variables in JQ queries

I want to use the value of a variable USER_PROXY in the JQ query statement.
export USER_PROXY= "proxy.zyz.com:122"
BY refering the SO answer no:1 from HERE , and also the LINK, I made the following shell script.
jq -r --arg UPROXY ${USER_PROXY} '.proxies = {
"default": {
"httpProxy": "http://$UPROXY\",
"httpsProxy": "http://$UPROXY\",
"noProxy": "127.0.0.1,localhost"
}
}' ~/.docker/config.json > tmp && mv tmp ~/.docker/config.json
However, I see I get the bash error as below. What is it that is missing here. Why is JQ variable UPROXY not getting the value from USER_PROXY bash variable.
export USER_PROXY= "proxy.zyz.com:122"
You can't have a space here. This sets USER_PROXY to an empty string and tries to export a non-existant variable 'proxy.zyz.com:122'. You probably want
export USER_PROXY="proxy.zyz.com:122"
jq -r --arg UPROXY ${USER_PROXY} '.proxies = {
"default": {
"httpProxy": "http://$UPROXY\",
"httpsProxy": "http://$UPROXY\",
"noProxy": "127.0.0.1,localhost"
}
}' ~/.docker/config.json > tmp && mv tmp ~/.docker/config.json
You need quotes around ${USER_PROXY} otherwise any whitespace in it will break it. Instead use --arg UPROXY "${USER_PROXY}".
This isn't the syntax for using variables inside a string in jq. Instead of "...$UPROXY..." you need "...\($UPROXY)..."
You are escaping the " at the end of the string by putting a \ before it. I am not sure what you mean here. I think you perhaps meant to use a forward slash instead?
This last issue is the immediate cause of the error message you're saying. It says "syntax error, unexpected IDENT, expecting '}' at line 4" and then shows you what it found on line 4: "httpsProxy": .... It parsed the string from line 3, which looks like: "http://$UPROXY\"\n " because the escaped double quote doesn't end the string. After finding the end of the string on line 4, jq expects to find a } to close the object, or a , for the next key-value-pair, but it finds httpsProxy, which looks like an identifier. So that's what the error message is saying. It found an IDENTifier when it was expecting a } (or a , but it doesn't mention that).

jq editing JSON not changing value

I have a json file that I want to manipulate; but I can't seem to get the data to change as I want.
If I execute 1; the return is "true" because the folder is paused.
1: jq '.folders[] | select(.label=='\"$folder\"') | .paused' "$f"
If I execute 2; a single record is selected and the "true" is replaced with "false".
2: jq '.folders[] | select(.label=='\"$folder\"') .paused = false' "$f"
If I execute 3; the entire file is returned but no change is made.
3: jq 'if (.folders[] | .label == '\"$folder\"') then .paused = false else . end' "$f"
I want the entire file returned with the change made so I can post it back to update the config I'm trying to change.
What am I doing wrong here?
You need to use select on the label selection, otherwise the context changes and you can't return to the whole object.
(.folders[] | select (.label == '\"$folder\"')).paused = false
Also, it's cleaner to propagate the variable value as a variable into jq instead of handling or corner cases of quoting yourself:
jq --arg folder "$folder" '(.folders[] | select (.label == $folder)).paused = false' "$f"
All working now!
See: Syncthing forum for more info.
I am trying to pause / resume specific folders on a schedule, using cron. This is not supported by the API yet so I needed to do it manually by editing the config.
Thanks for all the help.

Strange interaction between print and the ternary conditional operator

Ran into a strange interaction between print and the ternary conditional operator that I don't understand. If we do...:
print 'foo, ' . (1 ? 'yes' : 'no') . ' bar';
...then we get the output...:
foo, yes bar
...as we would expect. However, if we do...:
print (1 ? 'yes' : 'no') . ' bar';
...then we just get the output...:
yes
Why isn't " bar" getting appended to the output in the second case?
Let's do it, but for real -- that is, with warnings on
perl -we'print (1 ? "yes" : "no") . " bar"'
It prints
print (...) interpreted as function at -e line 1.
Useless use of concatenation (.) or string in void context at -e line 1.
yes
(but no newline at the end)
So since (1 ? "yes" : "no") is taken as the argument list for the print function then the ternary is evaluated to yes and that is the argument for print and so that, alone, is printed. As this is a known "gotcha," which can easily be done in error, we are kindly given a warning for it.
Then the string " bar" is concatenated (to the return value of print which is 1), what is meaningless in void context, and for what we also get a warning.
One workaround is to prepend a +, forcing the interpretation of () as an expression
perl -we'print +(1 ? "yes" : "no") . " bar", "\n"'
Or, call the print as function properly, with full parenthesis
perl -we'print( (1 ? "yes" : "no") . " bar", "\n" )'
where I've added the newline in both cases.
See this post for a detailed discussion of a related example and precise documentation links.
If the first non-whitespace character after a function name is an opening parenthesis, then Perl will interpret that as the start of the function's parameter list and the matching closing parenthesis will be used as the end of the parameter list. This is one of the things that use warnings will tell you about.
The usual fix is to insert a + before the opening parenthesis.
$ perl -e "print (1 ? 'yes' : 'no') . ' bar'"
yes
$ perl -e "print +(1 ? 'yes' : 'no') . ' bar'"
yes bar

How to check jq result is null or not?

I'm using jq to parse some JSON. I want to check whether a property exists or not. If it exists I always want to get the first element in the array. Based on this I want to use if then ... else.
My code looks like this:
JSON_INPUT='{"var1":[{"foo":"bar"}],"var2":[{"fooooo":"baaaaar"}]}'
VAR2=$(echo $JSON_INPUT | jq '.["var2"] | .[0]')
if [ -z "${VAR2}" ]
then
echo "does not exist"
# do some stuff...
else
echo "exist"
# do some stuff...
fi
The JSON_INPUT may contain var2 but must not. If it does not exist VAR2 will be null. But I'm not able to check for this null value. Where is the error?
Where is the error?
Replace
[ -z "${VAR2}" ]
with
[ "${VAR2}" = "null" ]
because jq returns string null if var2 is not found in JSON file.
Or use --exit-status:
if echo "$JSON_INPUT" | jq --exit-status '.var2' >/dev/null; then
echo "exists"
else
echo "does not exist"
fi
I want to check whether a property exists or not. If it exists I always want to get the first element in the array ...
The condition can best be expressed as:
if has("var2")
so since you seem to want VAR2 to be null if the condition is not met, you could write:
VAR2=$(jq 'if has("var2") then .["var2"][0] else null end')
If the condition is not met, then we would find:
echo "$VAR2"
null
There is a small problem, here, though: for example, what if the array's first value is null?
So a more discriminating approach would be to write:
VAR2=$(jq 'if has("var2") then .["var2"][0] else empty end')
Even if neither of these approaches is exactly what you want, they illustrate that putting as much of the logic inside the jq program as possible yields a tidy, easy-to-understand, and efficient solution.

Add an empty line prior to another matched line in jq?

Say I have a raw input like the following:
"```"
"include <stdio.h>"
"..."
"```"
"''some example''"
"*bob"
"**bob"
"*bob"
And I'd like to add a blank line right before the "*bob":
"```"
"include <stdio.h>"
"..."
"```"
"''some example''"
""
"*bob"
"**bob"
"*bob"
Can this be done with jq?
Yes, but to do so efficiently you'd effectively need jq 1.5 or higher:
foreach inputs as $line (0;
if $line == "*bob" then . + 1 else . end;
if . == 1 then "" else empty end,
$line)
Don't forget to use the -n command-line option!
Here is another solution which uses the -s (slurp) option
.[: .[["*bob"]][0]] + ["\n"] + .[.[["*bob"]][0]:] | .[]
that's a little unreadable but we can make it better with a few functions:
def firstbob: .[["*bob"]][0] ;
def beforebob: .[: firstbob ] ;
def afterbob: .[ firstbob :] ;
beforebob + ["\n"] + afterbob
| .[]
if the above filter is in filter.jq and the sample data is in data then
$ jq -Ms -f filter.jq data
produces
"```"
"include <stdio.h>"
"..."
"```"
"''some example''"
"\n"
"*bob"
"**bob"
"*bob"
One issue with this approach is that beforebob and afterbob won't quite work as you probably want if "*bob" is not in the input. The easiest way to address that is with an if guard:
if firstbob then beforebob + ["\n"] + afterbob else . end
| .[]
with that the input will be unaltered if "*bob" is not present.