lyrjsq
Last Updated: February 25, 2016
·
12.88K
· janosgyerik
32ef4e3e388cbadc756a008cade3ee6a

Extract your external IP using command line tools

To say the conclusion first,
(and thank you @alfredormz for that!)
the most elegant and fastest way is this:

dig +short myip.opendns.com @resolver1.opendns.com

This asks the IP address of myip.opendns.com from the name server resolver1.opendns.com (something you trust),
which will return your external IP address.

If you don't have dig,
you can use these alternatives:

curl ipecho.net/plain
curl icanhazip.com
curl curlmyip.com
curl l2.io/ip
curl ip.appspot.com
curl ifconfig.me/ip
curl eth0.me

Or if you ever need in JSON format:

curl httpbin.org/ip
curl wtfismyip.com/json

These work over HTTP,
and therefore less efficient than the direct DNS query with dig.

When using any of these solutions,
keep in mind that you are relying on external services.
They might be unavailable temporarily or even permanently at some point.
So none of these is perfect.

This article is actually about something else.
It's about the challenge of extracting the relevant bits from a short text.
Recently I needed to extract my external IP address from http://monip.org/ using command line tools.
This article is about explaining the thought process,
in case you ever need something similar.

Let's see what we get with a simple curl:

$ curl monip.org
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>MonIP.org v1.0</title>
<META http-equiv="Content-type" content="text/html; charset=ISO-8859-1">
</head>
<P ALIGN="center"><FONT size=8><BR>IP : 67.205.36.164<br></font><font size=3><i>genovese.dreamhost.com</i><br></font><font size=1><br><br>Pas de proxy d�tect� - No Proxy detected</font></html>

One way to extract the IP address from this is using sed:

$ curl -s monip.org | sed -n 's/.*IP : \([0-9.]*\).*/\1/p'
67.205.36.164�tect� - No Proxy detected</font></html>

Ugh. It almost works, but just almost:
there's some garbage left behind at the end.
I don't really know why the .* at the end didn't wipe out everything after the IP address.
It seems sed has problems with some non-ascii characters.
Looking at the original output,
it looks like the site uses ISO-8859-1 encoding,
so if we stick an iconv in the pipeline it might fix the problem:

$ curl -s monip.org | iconv -f iso-8859-1 | sed -n 's/.*IP : \([0-9.]*\).*/\1/p'
67.205.36.164

Much better! Let's go over how this sed works:

  • By default sed echoes the lines it processes.
    The -n flag turns that off:
    it will only print anything with explicit printing commands.

  • One technique for extracting a pattern from some body of text is using the idiom: s/.*\(pattern\).*/\1/.
    The \1 in the replacement is a back reference to the pattern that was matched within the first \(...\) expression.
    The .* before and after the pattern of interest are to make the search pattern match the entire line,
    so that the result of the replacement will be the entire line replaced with the pattern of interest.

  • We used .*IP : \([0-9.]*\).* as the pattern.
    This is an inaccurate, lazy approach,
    and we could have been much more accurate, for example:
    .*IP : \([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\)\>.*.
    It's up to you and the given task where you draw the line.

  • The p flag in the s///p idiom makes sed print the line only if there was a substitution.
    This is perfect for our purpose:
    this way only the line with the IP address will be printed,
    after the substitution,
    so the output will be simply the IP address itself.

By the way,
when I first so the encoding issue with sed my first reaction was using perl instead:

curl -s monip.org | perl -ne '/IP : ([0-9.]+)/ && print $1'

This uses the equivalent extraction logic as in the earlier sed,
with some differences due to the nature of these tools:

  • The -e flag is to execute a Perl expression given on the command line,
    as opposed to a specified file.

  • The -n flag wraps the expression inside a while (<>) { ... } block,
    making it suitable for processing standard input line by line.

  • The pattern matching is slighly simpler compared to sed,
    because sed uses basic regular expressions (BRE),
    and Perl uses PCRE, which is far more advanced.

  • In Perl, you can use back references to patterns captured within (...) expressions using the variables $1, $2, and so on.

  • The idiom /(pattern)/ && print $1 means,
    if the pattern was matched,
    then print the content of the first group.

13 Responses
Add your response

12982
10620301 745710515470370 5763874756421604255 o

Thanks for the tip. Don't know why, but on OSX I had to add a
LC_CTYPE=C &&
before every command, otherwise i got a sed error:
sed: RE error: illegal byte sequence

over 1 year ago ·
13003
512633c40c91049077f96b94f5982733

My favorite one is dig +short myip.opendns.com @resolver1.opendns.com

over 1 year ago ·
13006
32ef4e3e388cbadc756a008cade3ee6a

@alfredormz: that is awesome! Added to my post.

over 1 year ago ·
13009
D42a7264714dee5006b9c99d2567a320

Aliased, thanks! :)

alias myip='dig +short myip.opendns.com @resolver1.opendns.com'
over 1 year ago ·
13103
Df8e5930113ab9844be90e1271e153fb
13115
32ef4e3e388cbadc756a008cade3ee6a

@abimaelmartell lol that's funny, added to the list ;-)

over 1 year ago ·
13121
B5bd2b27257673d38a1b69679c5b1778

I prefer easy to remember hostnames. Most of the commands above you're going to forget when you're on some rando host, which is when you generally need these commands:
$ curl eth0.me

over 1 year ago ·
13123
32ef4e3e388cbadc756a008cade3ee6a

@stephen-wood I completely agree with you. And since curl eth0.me is very intuitive and easy to remember, I was just about to add at the very top, but.... It doesn't work, at least when I'm typing this :( I tried Europe and the US. Btw, I chose curl ipecho.net/plain as the first because it's the fastest for me. These things change over time, of course. Thanks anyway, I'm still adding to the list!

over 1 year ago ·
13147
302b653accce38918fa61de8486f9549

Cool add these to your .profile file and enjoy

# IP addresses
alias ip="dig +short myip.opendns.com @resolver1.opendns.com"
alias localip="ipconfig getifaddr en1"
alias ips="ifconfig -a | grep -o 'inet6\? \(addr:\)\?\s\?\(\(\([0-9]\+\.\)\{3\}[0-9]\+\)\|[a-fA-F0-9:]\+\)' | awk '{ sub(/inet6? (addr:)? ?/, \"\"); print }'"
over 1 year ago ·
13173
Picture ms

You may also find these tools useful: http://httpbin.org/

curl http://httpbin.org/ip

curl http://httpbin.org/user-agent

over 1 year ago ·
13174
32ef4e3e388cbadc756a008cade3ee6a

Thanks @maciejsmolinski, I added to the post! Btw, you know that you don't need spell out the http:// for curl, it can figure it out ;-)

over 1 year ago ·
17416
409302 10151131950910230 214813917 n 2

Here's another one. I've been using it for years:

$ type my-ip
my-ip is aliased to `wget http://checkip.dyndns.org/ -O - -o /dev/null | cut -f7 -d"<" | cut -f2 -d">" '

Output would be:

$ my-ip
Current IP Address: xxx.xxx.xxx.xxx
over 1 year ago ·
22592
5f751d3f08afa8971636a36a5db5ccdc