0

I'm trying to create 2 variables via bash $lat, $long base on the result of my curl response.

curl ipinfo.io/33.62.137.111 | grep "loc" | awk '{print $2}'

I got.

"42.6334,-71.3162",

I'm trying to get

  • $lat=42.6334

  • $long=-71.3162

Can someone give me a little push ?

1
  • Pro tip - you can request ipinfo.io/33.62.137.111/loc to just get the coordinates, which simplifies some of the existing answers Commented Apr 6, 2020 at 3:54

2 Answers 2

4
IFS=, read -r lat long < <(
  curl -s ipinfo.io/33.62.137.111 |
    jq -r '.loc'
)

printf 'Latitude is: %s\nLongitude is: %s\n' "$lat" "$long"

The ipinfo.io API is returning JSON data, so let parse it with jq:

Here is the JSON as returned by the query from your sample:

{
  "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"
}

We are going to JSON query the loc entry from the main root object ..

  • curl -s ipinfo.io/33.62.137.111: download the JSON data -s silently without progress.
  • jq -r '.loc': Process JSON data, query the loc entry of the main object and -r output raw string.
  • IFS=, read -r lat long < <(: Sets the Internal Field Separator to , and read both lat and long variables from the following command group output stream.
Sign up to request clarification or add additional context in comments.

7 Comments

Wow, that's impressive answer. ✨
What is jq ? and why we need < < ? What is the first one do vs. the second one ?
jq is a json parser <( is called process substitution which is specific to bash afaik.
Oh it is a package where I have to install. I will install now.
What is the . in front .loc does ?
|
1

Although the answer from @LeaGris is quite interesting, if you don't want to use an external library or something, you can try this:

Playground: https://repl.it/repls/ThoughtfulImpressiveComputer

coordinates=($(curl ipinfo.io/33.62.137.111 | sed 's/ //g' | grep -P '(?<=\"loc\":").*?(?=\")' -o | tr ',' ' '))

echo "${coordinates[@]}"

echo ${coordinates[0]}
echo ${coordinates[1]}

Example output:

39.9690 -83.0114 # echo "${coordinates[@]}"
39.9690 # ${coordinates[0]}
-83.0114 # ${coordinates[1]}

Explanation:

  1. curl ... get the JSON data

  2. sed 's/ //g' remove all spaces

  3. grep -P ... -o

    • -P interpret the given pattern as a perl regexp
    • (?<=\"loc\":").*?(?=\")
      • (?<=\"loc\":") regex lookbehind
      • .*? capture the longitude and latitude part with non-greedy search
      • (?=\") regex lookahead
    • -o get only the matching part which'ld be e.g. 39.9690,-83.0114
  4. tr ',' ' ' replace , with space

  5. Finally we got something like this: 39.9690 -83.0114

Putting it in parentheses lets us create an array with two values in it (cf. ${coordinates[...]}).

7 Comments

If you want to avoid parsing with jq you can extract with sed: read -r lat long < <(curl -s ipinfo.io/33.62.137.111 | sed -n 's/.*\"loc\"[[:space:]]*:[[:space:]]*\"\([[:digit:].]\+\),\([[:digit:].]\+\)\".*/\1 \2/p')
@LéaGris that looks also good! But you can't be sure that let's say a space will follow right after the "loc" key, in-between or after the ":" etc., so I wanted to make sure that the parsing is kinda flexible.
This is why the * (0 or more) amount of spaces. They are optional per the JSON syntax.
Yes, my apologies. I just noticed that. Thanks.
Anyway, it is very hard to properly parse JSON syntax with a RegEx, even my example has remaining failure cases like if the key and value are on different lines or if the JSON string uses Unicode notations or whatever the already verbose RegEx can't replace a true JSON parser.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.