Bash looping over returned Json using curl - bash

So i make a curl command to a url which returns an array of objects
response= $(curl --locaiton --request GET "http://.....")
I need to iterate over the returned json and extract a single value..
The json is as follows:
{
data:[
{"name": "ABC", "value": 1},
{"name": "EFC", "value": 4},
{"name": "CEC", "value": 3}
]
}
Is there anyway in BASh i can extract the second object value.. by perhaps iterating and doing an IF

Use jq
A simple example to extract the 2nd entry of the array would be:
RESPONSE='{"data":[{"name":"ABC","value": 1},{"name":"EFC","value":4},{"name":"CEC","value":3}]}';
EXTRACTED=$(echo -n "$RESPONSE" | jq '.data[1]');
echo $EXTRACTED

jq is the most commonly used command/tool to parse JSON data from a shell script. Here is an example with your data:
#!/usr/bin/env sh
# JSON response
response='
{
"data": [
{"name": "ABC", "value": 1},
{"name": "EFC", "value": 4},
{"name": "CEC", "value": 3}]
}'
# Name of entry
name='EFC'
# Get value of entry
value=$( jq --null-input --raw-output --arg aName "$name" \
"$response"' | .data[] | select(.name == $aName) | .value')
# Print it out
printf 'value for %s is: %s\n' "$name" "$value"
Alternatively jq can be used to transform the whole JSON name value objects array, into a Bash associative array declaration:
#!/usr/bin/env bash
# JSON response
response='
{
"data": [
{"name": "ABC", "value": 1},
{"name": "EFC", "value": 4},
{"name": "CEC", "value": 3}]
}'
# Name of entry
name='EFC'
# Covert all JSON array name value entries into a Bash associative array
# shellcheck disable=SC2155 # safe generated declaration
declare -A entries="($( jq --null-input --raw-output \
"$response"' | .data[] | ( "[" + ( .name | #sh ) + "]=" + (.value | #sh) )'))"
# Print it out
printf 'value for %s is: %s\n' "$name" "${entries[$name]}"

Related

jq: iterate over every element of list and replace it with value

I've got this json-file:
{
"name": "market",
"type": "grocery",
"shelves": {
"upper_one": [
"23423565",
"23552352",
"08789089"
]
}
}
I need to iterate over every element of an list (upper_one), and replace it with other value.
I've tried this code:
#/bin/bash
for product in $(cat first-shop.json| jq -r '.shelves.upper_one[]')
do
cat first-shop.json| jq --arg id "$((1 + $RANDOM % 10))" --arg product "$product" -r '.shelves.upper_one[]|select(. == $product)|= $id'
done
But I got this kind of output:
1
23552352
08789089
23423565
10
08789089
23423565
23552352
7
Is it possible to iterate over list with jq, replace values with value from another function (like $id in the code), and print the whole final json with substituted values?
I need this kind of output:
{
"name": "market",
"type": "grocery",
"shelves": {
"upper_one": [
"1",
"10",
"7"
]
}
}
not just elements of "upper_one" list thrice.
You could try the following script :
#!/usr/bin/env bash
for product in $(jq -r '.shelves.upper_one[]' input.json)
do
id="$((1 + $RANDOM % 10))"
newIds+=("$id")
done
jq '.shelves.upper_one = $ARGS.positional' input.json --args "${newIds[#]}"
IMHO its better to use some scripting language and manipulate objects programmatically. If bash and jq is your only option - this do the job though not nice
$ jq '.shelves.upper_one[] |= (sub("23423565";"1") | sub("23552352";"10") | sub("08789089";"7"))' your.json
{
"name": "market",
"type": "grocery",
"shelves": {
"upper_one": [
"1",
"10",
"7"
]
}
}
consider conversion to numbers with | tonumber

Loop json Output in shell script

I have this output variable
OUTPUT=$(echo $ZONE_LIST | jq -r '.response | .data[]')
The Output:
{
"accountId": "xyz",
"addDate": "2020-09-05T10:57:11Z",
"content": "\"MyContent\"",
"id": "MyID",
"priority": null
}
{
"accountId": "xyz",
"addDate": "2020-09-05T06:58:52Z",
"content": "\"MyContent\"",
"id": "MyID",
"priority": null
}
How can I create a loop for this two values?
MyLoop
echo "$content - $id"
done
I tried this, but then I get a loop through every single value
for k in $(echo $ZONE_LIST | jq -r '.response | .data[]'); do
echo $k
done
EDIT 1:
My complete JSON:
{
"errors": [],
"metadata": {
"transactionId": "",
},
"response": {
"data": [
{
"accountId": "xyz",
"addDate": "2020-09-05T10:57:11Z",
"content": "\"abcd\"",
"id": "myID1",
"lastChangeDate": "2020-09-05T10:57:11Z",
},
{
"accountId": "xyz",
"addDate": "2020-09-05T06:58:52Z",
"content": "\"abc\"",
"id": "myID2",
"lastChangeDate": "2020-09-05T07:08:15Z",
}
],
"limit": 10,
"page": 1,
"totalEntries": 2,
},
"status": "success",
"warnings": []
}
Now I need a loop for data, because I need it for a curl
The curl NOW:
curl -s -v -X POST --data '{
"deleteEntries": [
Data_from_json
]
}' https://URL_to_Update 2>/dev/null)
Now I want to create a new variable from my JSON data. My CURL should look like this at the end:
curl -s -v -X POST --data '{
"deleteEntries": [
{
"readID": "myID1",
"date": "2020-09-05T10:57:11Z", <--Value from addDate
"content": "abcd"
},
{
"readID": "myID2",
"date": "2020-09-05T06:58:52Z", <--Value from addDate
"content": "abc"
}
]
}' https://URL_to_Update 2>/dev/null)
Something like:
#!/usr/bin/env bash
while IFS=$'\37' read -r -d '' id content; do
echo "$id" "$content"
done < <(
jq -j '.response | .data[] | .id + "\u001f" + .content + "\u0000"' \
<<<"$ZONE_LIST"
)
jq -j: Forces a raw output from jq.
.id + "\u001f" + .content + "\u0000": Assemble fields delimited by ASCII FS (Hexadecimal 1f or Octal 37), and end record by a null character.
It then becomes easy and reliable to iterate over null delimited records by having read -d '' (null delimiter).
Fields id content are separated by ASCII FS, so just set the Internal Field Separator IFS environment variable to the corresponding octal IFS=$'37' before reading.
The first step is to realize you can turn the set of fields into an array like this using a technique like this:
jq '(.accountId + "," + .addDate)'
Now you can update your bash loop:
for k in $(echo $ZONE_LIST | jq -r '.response | .data[]' | jq '(.content + "," + .id)'); do
echo $k
done
There is probably a way to combine the two jq commands but I don't have your original json data for testing.
UPDATE - inside the loop you can parse the comma-delimited string into separate fields. This are more efficient ways to handle this task but I prefer simplicity.
ID=$(echo $k | cut -d',' -f1)
PRIORITY=$(echo $k | cut -d',' -f2)
echo "ID($ID) PRIORITY($PRIORITY)"
Try this.
for k in $(echo $ZONE_LIST | jq -rc '.response | .data[]'); do
echo $k|jq '.content + " - " + .id' -r
done

jq how to get the last entry in an object

I'm working with jq 1.6 to get the last entry in an object. It should work like this:
data='{ "1": { "a": "1" }, "2": { "a": "2" }, "3": { "a": "3" } }'
result=`echo $data | jq 'myfilter'`
echo $result
{ "3": { "a": "3" } }
I tried these filters:
jq '. | last' # error: Cannot index object with number
How can I tell jq to quote the number?
jq '. | to_entries | last' # { "key": "3", "value": { "a": "3" } }
I guess I could munge this up by concatenating the key and value entries. Is there a simpler way?
The tutorial and the manual didn't help. No joy on SO either.
You can use the following :
jq 'to_entries | [last] | from_entries'
Try it here.
We can't use with_entries(last) because last returns a single element and from_entries requires an array, hence the [...] construct above.

why i get this error jq: error Cannot index array with string from json file?

i'm trying to build script that takes specific attribute value and store it in the array , this is the following JSON file:
[
{
"id": 1,
"name": "myna",
"description": "Simple Question",
"speaker": "USER",
},
{
"all_Id's": [
"11111"
],
"user": "me",
},
{
"id": 2,
"name": "mry",
"description": "Simple",
"speaker": "aaa",
}
]
as you see object in json file don't have the same attributes so i'm looking only on object has "name " attribute,the following script reads the Json file and return the values of attribute name only ,but i build something wrond as theERROR always on the "{" of the last object in file I don't know why , what i am i doing wrong?
the expected output is : [myna, mry]
#!/bin/bash
declare -a OB_I=()
declare counter1=0
jq -r '.name' file.json ; while read -r val ; do
if [[ ! $val ]]
then
OB_I[$counter]=$val ;
counter=$((counter+1));
fi
done;
$ printf '%s\n' "${OB_I[#]}"
The input of jq is a list, which doesn't have any keys, let alone one named name. You want
jq -r '.[].name'
instead.
Unrelated, you don't need the variable counter. You can simply append to your array with OB_I+=("$val").

How to manipulate a jq output using bash?

I have the following jq code snippet:
https://jqplay.org/s/QzOttRHoz1
I want to loop each element from the result array using bash such as the pseudo code shows:
#!/bin/bash
foreach result
print "My name is {name}, I'm {age} years old"
print "--"
The result would be:
My name is A, I'm 1 years old.
---
My name is B, I'm 2 years old.
---
My name is C, I'm 3 years old.
---
Of course this is a trivial example just to clarify that my goal is to manipulate each array from the jq result individually.
Any suggestions on how to write the pseudo code into valid bash statements?
Saving the json:
{
"Names": [
{ "Name": "A", "Age": "1" },
{ "Name": "B", "Age": "2" },
{ "Name": "C", "Age": "3" }
]
}
as /tmp/input.txt I can run:
</tmp/input.txt jq --raw-output 'foreach .Names[] as $name ([];[];$name | .Name, .Age )' \
| while read -r name && read -r age; do
printf "My name is %s, I'm %d years old.\n" "$name" "$age";
printf -- "--\n";
done
The --raw-output with | .Name, .Age just prints two lines per .Names array member, one with name and another with age. Then I read two lines at a time with while read && read and use that to loop through them.
If you rather have:
["A","1"]
["B","2"]
["C","3"]
that's sad, the best would be to write a full parser that would take strings like "\"" into account. Anyway then you can:
</tmp/input2.txt sed 's/^\[//;s/\]$//;' \
| while IFS=, read name age; do
name=${name%\"};
name=${name#\"};
age=${age%\"};
age=${age#\"};
printf "My name is %s, I'm %d years old.\n" "$name" "$age";
printf -- "--\n";
done
The first sed removed the leading and enclosing [ and ] in each line. Then I read two strings separated by , (so vars like "a,b","c,d" will be read incorrectly). Then these two strings are stripped of leading and enclosing ". Then the usuall printf is used to output the result.
I have a written a simple script to achieve what you need:
My Json file test.json which is similar to your snippet:
{
"Names": [
{ "Name": "A", "Age": "1" },
{ "Name": "B", "Age": "2" },
{ "Name": "C", "Age": "3" }
]
}
My script:
#!/bin/bash
for i in $(cat test.json | jq -r '.Names[] | #base64'); do
_jq() {
echo ${i} | base64 --decode | jq -r ${1}
}
echo "My Name is $(_jq '.Name'), I'm $(_jq '.Age') years old"
done
Note that foreach .Names[] as $name ([];[];$name | .Name, .Age )
can be simplified to:
.Names[] | ( .Name, .Age )
or even in this specific case to:
.Names[][]
or for that matter to:
.[][][]
The important point, however, is that foreach is not needed to achieve simple iteration.

Resources