Overall Goals:
- I take user input, as country and/or city variables, then use those variables to filter down the JSON data to only the relays that meet the users choice. (Probably do some regex in JQ when selecting with the user input variables, to make sure someone can input
New York
and it would matchNew York, NY
) - I then want to select a random relay in the relay array, or 1 random relay in each cities relay array, which is where I am getting stuck.
- After I get a random relay in that/each array, I want to print certain values based on the key (i.e. ipvr_addr_in and public_key), in a csv format (for now).
- If there are multiple relays at this point, I would iterate through them to find the lowest latency, sort the list, choose the lowest and pass that IP,key pair on to the final configuration.
Originally, I thought I'd just grab the first object in the array and use that:
country_choice=UK
city_choice=London
if [[ -z "$city_choice" ]]; then
jq --arg country_choice "$country_choice" -r '.countries[] | select(.name==$country_choice) | .cities[] | .relays[:1] | .ipv4_addr_in "," .public_key' testdata.json
else
jq --arg country_choice "$country_choice" --arg city_choice "$city_choice" -r '.countries[] | select(.name==$country_choice) | .cities[] | select(.name==$city_choice) | .relays[:1] | .ipv4_addr_in "," .public_key' testdata.json
fi
But that resulted in the JQ error: Cannot index array with string "public_key"
I am still very new to JQ and JSONs, so I am unfamiliar with why I can't print out specific values after filtering down to a specific object of several arrays.
Can anyone help me out here? I can't seem to find anything online about "selecting random" in jq.
Also any help with some of my other overall goals would be very much appreciated, I'm no coding guru.
An abridged test dataset (testdata.json):
{
"countries": [
{
"name": "UK",
"code": "gb",
"cities": [
{
"name": "London",
"code": "lon",
"relays": [
{
"hostname": "gb4-wireguard",
"ipv4_addr_in": "141.98.252.130",
"ipv6_addr_in": "2a03:1b20:7:f011::a01f",
"public_key": "IJJe0TQtuQOyemL4IZn6oHEsMKSPqOuLfD5HoAWEPTY="
},
{
"hostname": "gb5-wireguard",
"ipv4_addr_in": "141.98.252.222",
"ipv6_addr_in": "2a03:1b20:7:f011::a02f",
"public_key": "J57ba81Q8bigy9RXBXvl0DgABTrbl81nb37GuX50gnY="
},
{
"hostname": "gb-lon-wg-101",
"ipv4_addr_in": "146.70.119.66",
"ipv6_addr_in": "2001:ac8:31:f007::a39f",
"public_key": "MPZX0ZQtB5r1pmcvIcsAt1AMvenios2ICBz9rjbN/l4="
}
]
},
{
"name": "Manchester",
"code": "mnc",
"relays": [
{
"hostname": "gb45-wireguard",
"ipv4_addr_in": "146.70.133.34",
"ipv6_addr_in": "2001:ac8:8b:2b::a45f",
"public_key": "v dOPx0FM8lGCjJ7m/7miWy67PGuazYYzvJoeMb97n4="
},
{
"hostname": "gb46-wireguard",
"ipv4_addr_in": "146.70.133.66",
"ipv6_addr_in": "2001:ac8:8b:2c::a46f",
"public_key": "2bciRobW0TPtjrZ2teilr 7PjyiBMUGfixvAKOE52Xo="
},
{
"hostname": "gb-mnc-wg-001",
"ipv4_addr_in": "146.70.133.98",
"ipv6_addr_in": "2001:ac8:8b:2d::a47f",
"public_key": "Q2khJLbTSFxmppPGHgq2HdxMQx7CczPZCgVpYZMoNnM="
}
]
}
]
},
{
"name": "USA",
"code": "us",
"cities": [
{
"name": "New York, NY",
"code": "nyc",
"relays": [
{
"hostname": "us97-wireguard",
"ipv4_addr_in": "86.106.143.210",
"ipv6_addr_in": "2a0d:5600:24:a94::a97f",
"public_key": "5fzEFqyRqc6qa1QPngIBK1gmWc0ex1Bpot/f6RqZPmc="
},
{
"hostname": "us98-wireguard",
"ipv4_addr_in": "86.106.143.223",
"ipv6_addr_in": "2a0d:5600:24:a95::a98f",
"public_key": "bo50ppMvVlNG4S6zqgd/J5l1Ce7Og89u wR10OvJrQ4="
},
{
"hostname": "us99-wireguard",
"ipv4_addr_in": "86.106.143.236",
"ipv6_addr_in": "2a0d:5600:24:a96::a99f",
"public_key": "EPLh6pVel06dND8cE4Prix9GP4hGLYNhQhn5mSN2yzM="
}
]
},
{
"name": "Seattle, WA",
"code": "sea",
"relays": [
{
"hostname": "us274-wireguard",
"ipv4_addr_in": "138.199.43.78",
"ipv6_addr_in": "2a02:6ea0:d80b:2::b74f",
"public_key": "ujasJmDuU0t4y6JmBLrdDxakKuaHvPRupRDfyywSWyw="
},
{
"hostname": "us-sea-wg-001",
"ipv4_addr_in": "138.199.43.91",
"ipv6_addr_in": "2a02:6ea0:d80b:3::b75f",
"public_key": "bZQF7VRDRK/JUJ8L6EFzF/zRw2tsqMRk6FesGtTgsC0="
},
{
"hostname": "us-sea-wg-003",
"ipv4_addr_in": "138.199.43.65",
"ipv6_addr_in": "2a02:6ea0:d80b:1::b73f",
"public_key": "4ke8ZSsroiI6Sp23OBbMAU6yQmdF3xU2N8CyzQXE/Qw="
}
]
}
]
}
]
}
CodePudding user response:
.relays[1:]
gives you an array of all but the first item. Use .relays[0]
instead to get only the first item:
.countries[] | select(.name==$country_choice)
| .cities[] | select(.name==$city_choice)
| .relays[0] | .ipv4_addr_in "," .public_key
141.98.252.130,IJJe0TQtuQOyemL4IZn6oHEsMKSPqOuLfD5HoAWEPTY=
As for a random value, jq does not have a random number generator. You could, however, (poorly) emulate one based on the current time using now
, or import one generated outside using --arg
or --argjson
.
Here's one very simple jq-only implementation of your script importing the $RANDOM
variable from bash
, which "expands to a random integer between 0 and 32767" (so make sure your arrays are not longer than that):
country_choice=UK # setting the country
city_choice= # omitting the city
jq --arg country_choice "$country_choice" --argjson country_random $RANDOM \
--arg city_choice "$city_choice" --argjson city_random $RANDOM \
--argjson relay_random $RANDOM -r \
'
.countries
| if $country_choice == ""
then .[$country_random % length]
else .[] | select(.name == $country_choice) end
| .cities
| if $city_choice == ""
then .[$city_random % length]
else .[] | select(.name == $city_choice) end
| .relays | .[$relay_random % length]
| .ipv4_addr_in "," .public_key
'
146.70.133.98,Q2khJLbTSFxmppPGHgq2HdxMQx7CczPZCgVpYZMoNnM=
Demo (with faked random numbers)
CodePudding user response:
Since the question envisions requiring several or an a priori unknown number of random values, the following illustration may be of interest.
< /dev/urandom tr -cd '0-9' | fold -w 1 | jq -MRcnr '
# Output: a prn in range(0;$n) where $n is `.`
def prn:
if . == 1 then 0
else . as $n
| ([1, (($n-1)|tostring|length)]|max) as $w
| [limit($w; inputs)] | join("") | tonumber
| if . < $n then . else ($n | prn) end
end;
# An illustration - 10 selections at random with replacement
[range(0;10) | ["a", "b", "c"] | .[length|prn]]
'
Notice that STDIN is used for the entropy source; file inputs would have to be handled using command-line options such as --rawfile
or --slurpfile
.
In fact, if one requires an a priori
unknown number of random values, then there really isn't any other practical choice when using the current version of jq.