Work the Shell - Exploring Lat/Lon with Shell Scripts

Never get lost at the command line again.

(The invocation is substitute/old-pattern/new-pattern/.)

Now we've got what we set out to create initially. Let's try it with yet another address:

$ sh 1313 S. Disneyland Drive, Anaheim CA

Yep, that's the parking structure for Disneyland in California.

Distance between Two Points

Now comes the hard part of this, actually. We can get the lat/lon of any address we desire, but calculating the distance between two points is a bit more tricky, as the mathematics involved is rather hairy, because what we're basically going to do is measure relative to the circumference of Earth.

I found a formula in JavaScript on-line as a starting point:

var R    = 6371;        // kilometers
var dLat = (lat2-lat1);
var dLon = (lon2-lon1);
var a    = Math.sin(dLat/2) * Math.sin(dLat/2) +
           Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
           Math.sin(dLon/2) * Math.sin(dLon/2);
var c    = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d    = R * c;

In this case, the circumference is R, and it's 6,371km. Because Earth is an oblate spheroid, not a perfect sphere, I expect this will have some small level of error, but let's proceed and see where we get.

To accomplish any sophisticated mathematics in a Linux shell, we're pretty much stuck with bc, but it's plenty powerful enough for this task, even if it's a bit clunky.

As an example, here's how you'd set the value of pi within a bc script:

pi=$(echo "scale=10; 4*a(1)" | bc -l)

The first stumble we have is that bc wants to work with radians, not degrees, but the lat/lon values we're getting are in degrees, so we need to convert them.

But before we do that, here's the intermediate output we seek, as we now need to work with two addresses, not just one:

$ sh \
  "1600 pennsylvania ave, washington dc" \
  "1313 s. disneyland drive, anaheim, ca"

Lat/long for 1600 pennsylvania ave, washington dc

= 38.89859, -77.035971

Lat/long for 1313 s. disneyland drive, anaheim, ca

= 33.814413, -117.924424

Next month, we'll crack open the script to see how I am working with two addresses at the same time and splitting it into the four variables we'll later need. Then, we'll look at how to use bc to do the math.

Dave Taylor has been involved with UNIX since he first logged in to the on-line network in 1980. That means that, yes, he's coming up to the 30-year mark now. You can find him just about everywhere on-line, but start here: In addition to all his other projects, Dave is now a film critic. You can read his reviews at


Dave Taylor has been hacking shell scripts for over thirty years. Really. He's the author of the popular "Wicked Cool Shell Scripts" and can be found on Twitter as @DaveTaylor and more generally at


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.


Anonymous's picture

When I run this script I get:
whereis: 7: Syntax error: Unterminated quoted string

Here is the script as I have it:



addr="$(echo $* | sed 's/ /+/g')"
  curl -s "$CONVERTER$ADDR" | \
      sed -e 's/.*{\("Lat":[^}]*\).*/\1/' -e 's/"L.."://g'
exit 0

Here is the command I'm running:
sh whereis 2001 Blake Street, Denver, CO

This seems pretty simple but I cannot see what I'm missing.

Cupla Problems

Mitch Frazier's picture

You've got a couple problems in your script, the one that's giving you the error is here:


You've got a string with mismatched quotes: it starts with a single quote and ends with a double quote, you need this:

#                                 ^

or this

#    ^

The other problem is that the shell is case sensitive, so you can't use "converter" and "CONVERTER" interchangeably, pick one. Same with "addr" and "ADDR". So change:

  curl -s "$CONVERTER$ADDR" | \


  curl -s "$converter$addr" | \

p.s. The first error (the mismatched quotes) was in the original text, it has now been fixed.

Mitch Frazier is an Associate Editor for Linux Journal.

What about bad addresses?

Grishnakh's picture

Is there any way to tell if the address you input is invalid? I've tried a few made-up bad addresses and it still seems to give valid lat/lon coordinates.

bad address

Anonymous's picture

That's a "Feature" of yahoo's service. They'll give you their best estimate. If the house number is bad, they'll tell you the location of the street; if the street is bad, they'll tell you the location of the city; if the city is bad, they'll tell you the location of the state....

To tell if the address is bad you could, perhaps, compare the results with the results of a less specific look up.

shell errors

zeugma's picture

As written, I get only longitude.

when I change the sed to...

    cut -d\" -f11,13 | \

I get the expected output

$ cat

ADDR="$(echo $* | sed 's/ /+/g')"

curl -s "$CONVERTER$ADDR" | \
    cut -d\" -f11,13 | \
    sed 's/[^0-9\.\,\-]//g;s/,$//'
exit 0
$ ./ 1313 S Disneyland Drive, Anaheim, CA

Response Is Different

Mitch Frazier's picture

The response you're getting is different, the GeoID value is not a quoted value as above. The following would avoid problems related to quotes:

  curl -s "$CONVERTER$ADDR" | \
      sed -e 's/.*{\("Lat":[^}]*\).*/\1/' -e 's/"L.."://g'

Mitch Frazier is an Associate Editor for Linux Journal.

Geek Guide
The DevOps Toolbox

Tools and Technologies for Scale and Reliability
by Linux Journal Editor Bill Childers

Get your free copy today

Sponsored by IBM

Upcoming Webinar
8 Signs You're Beyond Cron

Scheduling Crontabs With an Enterprise Scheduler
11am CDT, April 29th
Moderated by Linux Journal Contributor Mike Diehl

Sign up now

Sponsored by Skybot