Select multiple values from a split string in JQ - json

I see questions about selecting multiple values from an array using JQ, but I have a string that originally I just need the value after the last /, which is easily selected:
Input:
https://www.googleapis.com/compute/v1/projects/test-project-1/zones/europe-west1-b/instanceGroups/test-instance-group-1
JQ:
jq -r '.[]|.zone|=split("/")[-1]|"\(.name) \(.zone)"'
Output:
test-instance-group-1 europe-west1-b
However for the actual instances, the zone isn't listed, so must be extracted from the same key instance.
Input:
https://www.googleapis.com/compute/v1/projects/test-project-1/zones/europe-west1-b/instances/test-instance-1
JQ:
jq -r '.[]|.instance|=split("/")[-1]|"\(.instance)'
Output:
test-instance-1
However, I also want to extract the zone infomation, from the input as well, which I presume is selected with =split("/")[-3] however no matter how I format the request to JQ, I get errors:
$ jq -r '.[]|.instance|=split("/")[-1][-3]|"\(.instance)"'
jq: error (at <stdin>:47): Cannot index string with number
How can I extract two strings, from the same value/key ?

You're looking for something like this:
jq -r '.[].instance | split("/") | "\(.[-1]) \(.[-3])"'

Related

Jq parse error: Invalid numeric literal at line 1, column 9

I am trying to get values from a json file from a url using curl and then printing specific keys with jq command (e.g. company). Unfortunately when I use:
jq '.[] | .company' JB.json
I get the error:
parse error: Invalid numeric literal at line 1, column 9. I have checked the downloaded file with less and it looks exactly like in the url.
Some people suggested to use the -R option but it prints:
jq: error: Cannot iterate over string
The url of the file is: https://jobs.github.com/positions.json?description=python&location=new+york
If you use curl -sS to retrieve the file, the '//'-style comments are skipped:
curl -Ss 'https://jobs.github.com/positions.json?description=python&location=new+york' | jq '.[] | .company'
"BentoBox"
"Aon Cyber Solutions"
"Sesame"
"New York University"
So presumably your JB.json contains the "//"-style comments. The simplest workaround would probably be to filter out those first two lines (e.g. using sed (or jq!)) first.
Here's a jq-only solution:
< JB.json jq -Rr 'select( test("^//")|not)' |
jq '.[] | .company'

Cannot match integer value using regex - error (at <stdin>:6): number not a string or array

I have multiple JSON files with a person's age and I want to match specific ages using regex, however, I cannot be able to match even a single integer in a file.
I can select age using following jq,
jq -r .details.Age
I can match Name using following jq,
jq -r 'select(.details.Name | match("r.*"))'
But when I try to use test or match with Age I get following error,
jq -r 'select(.details.Age | match(32))'
jq: error (at <stdin>:6): number not a string or array
Here is code,
{
"details": {
"Age": 32,
"Name": "reverent"
}
}
I want to be able to match Age using jq something like this,
jq -r 'select(.details.Age | match(\d))'
Your .Age value is a number, but regexes work on strings, so if you really want to use regexes, you would have to transform the number to a string. This can be done using tostring, but please remember that the tostring representation of a JSON number might not always be what you think it will be.
–––
p.s. That should be match("\\d")

I cannot get jq to give me the value I'm looking for.

I'm trying to use jq to get a value from the JSON that cURL returns.
This is the JSON cURL passes to jq (and, FTR, I want jq to return "VALUE-I-WANT" without the quotation marks):
[
{
"success":{
"username":"VALUE-I-WANT"
}
}
]
I initially tried this:
jq ' . | .success | .username'
and got
jq: error (at <stdin>:0): Cannot index array with string "success"
I then tried a bunch of variations, with no luck.
With a bunch of searching the web, I found this SE entry, and thought it might have been my saviour (spoiler, it wasn't). But it led me to try these:
jq -r '.[].success.username'
jq -r '.[].success'
They didn't return an error, they returned "null". Which may or may not be an improvement.
Can anybody tell me what I'm doing wrong here? And why it's wrong?
You need to pipe the output of .[] into the next filter.
jq -r '.[] | .success.username' tmp.json
tl;dr
# Extract .success.username from ALL array elements.
# .[] enumerates all array elements
# -r produces raw (unquoted) output
jq -r '.[].success.username' file.json
# Extract .success.username only from the 1st array element.
jq -r '.[0].success.username' file.json
Your input is an array, so in order to access its elements you need .[], the array/object-value iterator (as the name suggests, it can also enumerate the properties of an object):
Just . | sends the input (.) array as a whole through the pipeline, and an array only has numerical indices, so the attempt to index (access) it with .success.username fails.
Thus, simply replacing . | with .[] | in your original attempt, combined with -r to get raw (unquoted output), should solve your problem, as shown in chepner's helpful answer.
However, peak points out that since at least jq 1.3 (current as of this writing is jq 1.5) you don't strictly need a pipeline, as demonstrated in the commands at the top.
So the 2nd command in your question should work with your sample input, unless you're using an older version.

Adding today's date into a CSV while converting a JSON to CSV using JQ

I have a JSON result from ElasticSearch that I parse with jq and retrieve the values I need out of the JSON and flatten it to CSV. There is no date in the fields coming from the JSON file. I need to be able to write a specific date as the first value in each row of the CSV.
ES_Query | jq -r '.aggregations.distinct_UUID.buckets[] | (.latest.hits.hits[]._source | [."_uuid",."site_name",."Jar"]) + (.PS_percentiles.values | [."80.0",."95.0"]) | #csv' >> /home/Outputs/res_wk_${end_date[$weeknum]}.csv
For example, in each row, I want to write the output of date +%F before the UUID, Sitename and Jar values. Is it possible?
B T W, I tried adding (date +%F) at the beginning and ending of the jq and got a compile time error.
There are basically two ways to proceed:
1) If your jq is sufficiently recent, use jq's time-and-date functions, starting with now:
$ jq -n now
1493069762.538462
$ jq -n 'now|strftime("%Y-%m-%d")'
"2017-04-24"
For further details, see the "Date" section of the online manual: https://stedolan.github.io/jq/manual/
2) Pass the date string in to jq using a suitable command-line option, e.g.
$ jq -n --arg date "$(date +%F)" '$date'
"2017-04-24"

Bash script traversing a multi-line JSON object using jq

I have to curl to a site (statuscake.com) that sends multiple items back in a JSON, each line of which contains multiple items. I want to extract from each line two of them, WebsiteName and TestID, so I can check if WebsiteName matches the one I'm interested in, get the TestID out and pass this to a second curl statement to delete the test.
Although it's more complex, the JSON that comes back is essentially of the form
[{"TestID": 123, "WebsiteName": "SomeSite1"}, {"TestID": 1234, "WebsiteName": "SomeSite2"}]
I can't seem to find a magic jq command to do it all in one - if there is one, I'd be really happy to see it.
I've got
cat $data | jq '[.[] | .WebsiteName]'
to get an array of the website names (and a very similar one for the TestIDs, but I think I've done something daft. data is the information coming back from the curl to get the JSON and that's populated OK.
I want to be able to assign these to two arrays, names and ids, then search names for the index of the relevant name, grab the id from ids and pass that to the curl. Unless there's a better way.
Any advice please?
My Xidel can do it all at once by selecting the JSON with a XPath-like query:
E.g. return all ids where the WebsiteName contains "site2" from an array of objects:
xidel /tmp/x.json -e '$json()[contains((.).WebsiteName, "site2")]/TestID'
Or e.g. to download the original JSON and then make the HTTP request with the ids:
xidel http://statuscake.com/your-url... -f '$json()[contains((.).WebsiteName, "site2")]/TestID!x"/your-delete-url{.}..."'
If I'm getting your question right, it sounds like what you want is to, for each element, select those where .WebsiteName == "needle", and then get .TestID from it. You can do just that:
.[] | select(.WebsiteName == "needle") | .TestID
If you want an array as the result, you can wrap the above script in square brackets.
The jq filters startswith and endswith may be of interest to you. If you're going to pass the result back to cURL, you may also be interested in the #sh formatting filter and the -r command-line flag.
Assuming you have a bash 4+ and assuming the json is valid (does not contain newlines in strings, etc.) this works:
$ echo "$data"
[{"TestID": 123, "WebsiteName": "SomeSite1"}, {"TestID": 1234, "WebsiteName":
"SomeSite2"}, {"TestID": 555, "WebsiteName": "foo*ba#r blah[54]quux{4,5,6}"}]
$ declare -A arr
$ while IFS= read -r line; do
eval "$line"
done < <(jq -M -r '.[] | #sh "arr[\(.WebsiteName)]+=\(.TestID)"' <<<"$data")
$ declare -p arr
declare -A arr='(["foo*ba#r blah[54]quux{4,5,6}"]="555" [SomeSite2]="1234" [SomeSite1]="123" )'
Here is a solution using only jq primitives.
.[]
| if .WebsiteName == "SomeSite1" then .TestID else empty end
This is essentially the same as Santiago's answer but if you are new to jq it may be informative because select/1 is defined as
def select(f): if f then . else empty end;