0

Let's assume, I have this package jq installed. So I ran this command

curl -s ipinfo.io/33.62.137.111

and get this result

{
  "ip": "33.62.137.111",
  "city": "Columbus",
  "region": "Ohio",
  "country": "US",
  "loc": "39.9690,-83.0114",
  "postal": "43218",
  "timezone": "America/New_York",
  "readme": "https://ipinfo.io/missingauth"
}

I know I can get city by doing this

curl -s ipinfo.io/33.62.137.111 | jq -r '.city' 

I know I can get region by doing this

curl -s ipinfo.io/33.62.137.111 | jq -r '. region' 

I'm trying to curl 7 times to create 7 variables.

Is there a way to create multiple variables based on the first curl response?

2
  • jq -r '.[]' maybe Commented Apr 3, 2020 at 1:30
  • @cyber8200 Do you require an answer that includes jq? If so, then please add the tag jq. Commented Apr 4, 2020 at 10:18

2 Answers 2

4

It is easy with Bash 4+ using an associative array:

#!/usr/bin/env bash

# Map the JSON response into an associative array
declare -A "assoc_array=($(
  curl -s ipinfo.io/33.62.137.111 |
    jq -r 'to_entries[] | "[\(.key | @sh)]=\(.value | @sh)"'
))"
IFS=, read -r assoc_array[lat] assoc_array[long] <<<"${assoc_array[loc]}"

echo "Here is how assoc_array was declared/created"
echo
typeset -p assoc_array
echo
echo

# Display the content of the associative array
echo "Here is a breakdown of all entries in the assoc_array:"
echo
for k in "${!assoc_array[@]}"; do
  printf '%q = %q\n' "$k" "${assoc_array[$k]}"
done

Sample output:

Here is how assoc_array was declared/created

declare -A assoc_array=([country]="US" [region]="Ohio" [city]="Columbus" [timezone]="America/New_York" [ip]="33.62.137.111" [lat]="39.9690" [readme]="https://ipinfo.io/missingauth" [long]="-83.0114" [loc]="39.9690,-83.0114" [postal]="43218" )


Here is a breakdown of all entries in the assoc_array:

country = US
region = Ohio
city = Columbus
timezone = America/New_York
ip = 33.62.137.111
lat = 39.9690
readme = https://ipinfo.io/missingauth
long = -83.0114
loc = 39.9690\,-83.0114
postal = 43218

For older Bash, it is a bit trickier but here it is

It separates values by the ASCII ETX (Value 3 for End of Text) and generates a stream of fields with jq, then read each variable into the predictable order. If a key is missing from the JSON response object, the field will be empty.

Contrarily to the associative array method, key names have to be known beforehand and provided in a predicted order (all this is handled by the long jq query).

#!/usr/bin/env bash

IFS=$'\3' read -r ip hostname city region country lat long postal timezone readme < <(
  curl -s ipinfo.io/33.62.137.111 |
    jq -r '"\(.ip+"\u0003")\(.hostname+"\u0003")\(.city+"\u0003")\(.region+"\u0003")\(.country+"\u0003")\(.loc | split(",") |"\(.[0]+"\u0003")\(.[1]+"\u0003")")\(.postal+"\u0003")\(.timezone+"\u0003")\(.readme+"\u0003")"'
)

printf 'ip = %q\n' "$ip"
printf 'hostname = %q\n' "$hostname"
printf 'city = %q\n' "$city"
printf 'region = %q\n' "$region"
printf 'country = %q\n' "$country"
printf 'latitude = %q\n' "$lat"
printf 'longitude = %q\n' "$long"
printf 'postal code = %q\n' "$postal"
printf 'timezone = %q\n' "$timezone"
printf 'readme = %q\n' "$readme"

Sample output:

ip = 33.62.137.111
hostname = ''
city = Columbus
region = Ohio
country = US
latitude = 39.9690
longitude = -83.0114
postal code = 43218
timezone = America/New_York
readme = https://ipinfo.io/missingauth
Sign up to request clarification or add additional context in comments.

Comments

1

You might be interested in the JSON-parser . You won't need curl, jq, or even a Bash-script for your use-case.

Retrieving the JSON:

$ xidel -s -H "Accept: application/json" "https://ipinfo.io/33.62.137.111" -e '$json'
$ xidel -s "https://ipinfo.io/33.62.137.111/json" -e '$json'
{
  "ip": "33.62.137.111",
  "city": "Columbus",
  "region": "Ohio",
  "country": "US",
  "loc": "39.9690,-83.0114",
  "postal": "43218",
  "timezone": "America/New_York",
  "readme": "https://ipinfo.io/missingauth"
}

Using Xidel's exclusive eval() function to create internal variables for every key-value pair, followed by Bash's built-in eval command to convert them to shell variables:

$ xidel -s "https://ipinfo.io/33.62.137.111/json" \
  -e '$json() ! `{.}:=$json/{.}`'
$ xidel -s "https://ipinfo.io/33.62.137.111/json" \
  -e 'for $key in map:keys($json) return `{$key}:=$json/{$key}`'
ip:=$json/ip
city:=$json/city
region:=$json/region
country:=$json/country
loc:=$json/loc
org:=$json/org
postal:=$json/postal
timezone:=$json/timezone
readme:=$json/readme

$ xidel -s "https://ipinfo.io/33.62.137.111/json" \
  -e '$json() ! eval(`{.}:=$json/{.}`)[0]' --output-format=bash
$ xidel -s "https://ipinfo.io/33.62.137.111/json" \
  -e "for $key in map:keys($json) return eval(`{$key}:=$json/{$key}`)[0]" \
  --output-format=bash
ip='33.62.137.111'
city='Columbus'
region='Ohio'
country='US'
loc='39.9690,-83.0114'
postal='43218'
timezone='America/New_York'
readme='https://ipinfo.io/missingauth'
result=

$ eval "$(
  xidel -s "https://ipinfo.io/33.62.137.111/json" \
  -e '$json() ! eval(`{.}:=$json/{.}`)[0]' --output-format=bash
)"

printf '%s\n' $ip $city $region $country $loc $postal $timezone $readme
33.62.137.111
Columbus
Ohio
US
39.9690,-83.0114
43218
America/New_York
https://ipinfo.io/missingauth

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.