7
\$\begingroup\$

I have a bash script, where I need to get the user's geolocation (latitude, longitude and country code). If a GPS device is present, I use gpspipe. But a lot of laptops/computers don't have GPS devices (yet). I need the location as accurate as possible, and getting the geolocation by IP address is (by far) too unreliable.

I ended up with the following bash script with the following dependencies: Python with sys, urllib and json libraries, wget and a working internet connection.

LAT=""
LNG=""
CTRY=""
IFS=$'\n'
printf "Give your location (street name, city, country, etc): " 
read LOCATION
while [ -z "$LAT" ]; do
    printf "Querying Google Geo API... "
    LOCATION=$(echo $LOCATION | python -c "exec(\"import urllib,sys;\\nprint(urllib.quote_plus(sys.stdin.readline()))\")")
    URL="https://maps.googleapis.com/maps/api/geocode/json?address=$LOCATION"
    GOOGLEAPIS=$(wget -qO- $URL)
    STATUS=$(echo $GOOGLEAPIS | python -c "exec(\"import json,sys;\\nobj=json.load(sys.stdin);\\nprint(obj['status'])\")")

    case "$STATUS" in
    *OK*)
        OPTIONS=($(echo $GOOGLEAPIS | python -c "exec(\"import json,sys;\\nobj=json.load(sys.stdin);\\nfor x in obj['results'][::]:print(x['formatted_address'])\")"))
        OPT=$OPTIONS
        NRFOUND=${#OPTIONS[@]}
        IDX="0"

        if [[ $NRFOUND -ge 2 ]]; then
            printf "More than one option found. Please select your location from the list:\n"
            select OPT in "${OPTIONS[@]}";
            do
                IDX=$(($REPLY - 1))
                break
            done
        fi
        LAT=$(echo $GOOGLEAPIS | python -c "exec(\"import json,sys;\\nobj=json.load(sys.stdin);\\nprint(obj['results'][$IDX]['geometry']['location']['lat'])\")")
        LNG=$(echo $GOOGLEAPIS | python -c "exec(\"import json,sys;\\nobj=json.load(sys.stdin);\\nprint(obj['results'][$IDX]['geometry']['location']['lng'])\")")
        CTRY=$(echo $GOOGLEAPIS | python -c "exec(\"import json,sys;\\nobj=json.load(sys.stdin);\\nfor x in obj['results'][$IDX]['address_components']:\\n\\tif x['types'][0] == 'country':print(x['short_name'])\")")
        break
        ;;
    *)
        printf "Google could not find your location based on your query. Google says: $STATUS\nPlease enter another location description.\n"
        read LOCATION
        ;;
    esac
done
unset $IFS
printf "Selected location: %s\nYour latitude/longitude location set to: (%s, %s).\nYour countycode is set to: %s\n" $OPT $LAT $LNG $CTRY

The code works (as required here on CR) and a sensible geolocation is found for a lot of queries. But, as I'm no seasoned shell writer, I'd like your review on this code. Do you see any pitfalls, corrections, improvements (on the coding), etc?

\$\endgroup\$
11
\$\begingroup\$

Bugs:

  • echo "$my_variable" | my_command can behave unexpectedly if the variable starts with one of the echo options. You can do my_command <<< "$my_variable" instead.
  • unset $IFS does not unset IFS. For that you need unset IFS.

Simplifications:

  • You shouldn't need to set IFS - read will save the full input to the variable anyway (see help [r]ead).
  • A lot of the code is already Python, and Python has much fewer caveats than Bash, so changing to it would very much benefit the maintainability of the code. Strictly speaking it's not a pure Bash script anyway, and you really don't want to be parsing JSON manually in Bash. If you really need a Bash script (which you shouldn't unless it's for a Bash programming exercise, and even then, somebody is wrong on the Internet if they asked you to use pure Bash to parse JSON) you could make it a wrapper script.
  • Bash's read takes a -p prompt option, so you can avoid the first printf.
  • Unchanging variables such as URL should be assigned outside of the loop.

Style issues:

  • Non-exported variables should be lowercase to distinguish them from exported variables.
  • It is customary to exit with a non-zero exit code when a command fails.
\$\endgroup\$
  • 6
    \$\begingroup\$ All good points. I'd especially emphasize: Just do this in Python since it's already a dependency. \$\endgroup\$ – Edward Sep 25 '14 at 12:42
  • 1
    \$\begingroup\$ Wow! Great comments! I understand and agree with all of them. I think I'll rewrite it in Python indeed. Again: thanks for your excellent review! \$\endgroup\$ – agtoever Sep 25 '14 at 12:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.