Linux ifconfig Golfing

A friend of mine recently showed me his version of extracting ip address(es) from ifconfig. The ifconfig tool on Linux is used to list IP addresses and configure network interfaces. It is sometimes necessary to capture your IP in a variable, like when writing some firewall rules.

I decided to show off and golf the extraction of IP address with four commonly used tools -- Awk, sed, a version of Perl everyone has, and the latest version of Perl 5.10.

His original version was pretty ugly:

$ ifconfig | perl -ple 'print $_ if /inet addr/ and $_ =~ s/.*inet addr:((?:\d+\.){3}\d+).*/$1/g  ;$_=""' | grep -v ^\s*$

My own version on my firewall was:

$ ifconfig | grep inet | awk -F: '{ print $2 }' | awk '{ print $1 }'

This looks nicer but I was uselessly using grep, and calling awk twice.

Golfing in Perl

My first attempt was to do it with grep and Perl.

$ ifconfig | grep inet | perl -ple '($_) = /addr:([^ ]+)/'
192.168.1.1
127.0.0.1

In a split second I realized that Perl does grep itself:

$ ifconfig | perl -nle '/addr:([^ ]+)/ and print $1'
192.168.1.1
127.0.0.1

Then I noticed that 'dr:' matches the same lines as 'addr:'; also 'and' can be replaced with '&&':

$ ifconfig | perl -nle '/dr:([^ ]+)/ && print $1'
192.168.1.1
127.0.0.1

The regular expression '([^ ]+)' can be replaced with '(\S+)', which matches non-whitespace characters:

$ ifconfig | perl -nle '/dr:(\S+)/ && print $1'
192.168.1.1
127.0.0.1

Cutting the whitespace, the final version for Perl is 37 characters long:

$ ifconfig|perl -nle'/dr:(\S+)/&&print$1'
192.168.1.1
127.0.0.1

Golfing in Perl 5.10

It can be made shorter with Perl 5.10, which introduces the new say function:

$ ifconfig|perl -nE'/dr:(\S+)/&&say$1'
192.168.1.1
127.0.0.1

The result for Perl 5.10 is 34 characters.

Golfing in sed

Then I also tried doing the same with sed:

$ ifconfig | sed -n '/dr:/{;s/.*dr://;s/ .*//;p;}'
192.168.1.1
127.0.0.1

I modified it to strip off everything that was not numbers and dots:

$ ifconfig | sed -n '/dr:/{;s/.*dr:\([0-9.]\+\) .*/\1/;p;}'
192.168.1.1
127.0.0.1

This turned out to be much longer, so I tried getting rid of backslashes, by enabling extended regexes in sed with -r argument:

$ ifconfig | sed -rn '/dr:/{;s/.*dr:([0-9.]+) .*/\1/;p;}'
192.168.1.1
127.0.0.1

I forgot that I had used the ([^ ]+) regex before. Another my friend reminded me this, shortening the sed script to:

$ ifconfig | sed -rn 's/.*r:([^ ]+) .*/\1/p'
192.168.1.1
127.0.0.1

Dropping the whitespace, sed version turned out to be 40 characters long.

Golfing in Awk

My final attempt was to optimize my original Awk version:

$ ifconfig | awk '/dr:/{gsub(/.*:/,"",$2);print$2}'
192.168.1.1
127.0.0.1

Cutting the whitespace, the Awk version is 43 characters.

The Winner

The winner in this golf tournament is Perl 5.10 with 34 chars!

Can You Do Better?

Can you golf this even shorter?

PS. my awk and sed cheat sheets might come handy!

Comments

Uriah Permalink
September 01, 2008, 02:49

Even shows ipv6 addresses, if you have them.

ip addr sh | awk '/inet/{print$2}'
Eddie Pasternak Permalink
September 01, 2008, 02:53

Oh dear it's so hard to put code and text on
the same page! (It's never done in books,
of course). I know! Let's put all the code
into little tiny scrolling text boxes! That'll
show em!!!!

September 01, 2008, 03:06

My first thought was the same as Uriah, though it ended up being a bit shorter anyway:

ip a | awk '/inet/{print $2}'

'ip a' is one of my most-oft-typed 4-character strings; I currently get 61 from `history | grep ip a | wc -l`.

September 01, 2008, 03:17

Uriah && Vineet Kumar, good one!

Eddie Pasternak, weeeeeee! Thanks for your feedback, that will be redesigned in the new version of catonmat!

September 01, 2008, 03:36

Umm, hello? netstat -in

September 01, 2008, 03:40

y, umm...

root@fw:~# netstat -in
Kernel Interface table
Iface   MTU Met   RX-OK RX-ERR RX-DRP RX-OVR   TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0   1500   0 2733196      0      0      0 2050902      0      0      0 BMNRU
eth1   1500   0 2056638      0      0      0 2728809      0      0      0 BMRU
eth1:  1500   0     - no statistics available -                        BMRU
lo    16436   0      17      0      0      0      17      0      0      0 LRU
September 01, 2008, 04:17

& will do just as well as && in the perl version, I suspect.

Daniel Luz Permalink
September 01, 2008, 04:51

Staying with ifconfig, Ruby's puts is one character longer than Perl 5.10's say, but uses the very same technique, showing the similarities between both languages:

ifconfig|ruby -ne'puts$1if/dr:(\S+)/'
NitinS Permalink
September 01, 2008, 06:08

ifconfig|grep Bcast|awk ' { print $2 }'|awk -F":" ' { print $2 }'

Ali Raza Memon Permalink
April 11, 2013, 04:47

Thank yew ! Owesome ;)

Kevin Permalink
September 01, 2008, 07:33

Here's a bash/grep version:

/sbin/ifconfig | grep "inet addr" | grep -v "ddr: " | while read a b c ; do echo $b ; done | ( IFS=':' ; while read a b ; do echo $b ; done )

September 01, 2008, 16:07

This is my version...
ifconfig|egrep -o 'dr:[^ ]+'|sed 's/dr://g'

September 01, 2008, 22:48

Here is the same in Scheme:

(let ([o (open-output-string)]) (parameterize ([current-output-port o]) (system "ifconfig")) (regexp-match* #rx"(?<= inet addr:)[0-9.]*" (get-output-string o)))

September 02, 2008, 17:12

Here's a Python version. We're going for the longest one-liner, right?:

ryant@spitfire:~$ ifconfig | python -c "import sys; import re; print re.match(re.compile('.*addr:([0-9\.]*)\s.*'), [ln for ln in sys.stdin.readlines() if ln.find('inet')>=0][0]).group(1)"
192.168.10.104
Nemelc Permalink
September 04, 2008, 11:35

40 characters

ifconfig|perl -nle's/dr:(\S+)/print$1/e'
September 04, 2008, 11:41

Oh yeah, Nemelc! Nice one! :)

September 08, 2008, 21:16

gethostip -d `hostname`

Bash Permalink
September 26, 2008, 17:01

ifconfig | awk '/inet addr/{print substr($2,6)}'

simpleton Permalink
September 30, 2008, 05:06

I don't understand the counting, so just compare the length:

ifconfig|grep Bc|cut -c21-31
ifconfig|perl -nE'/dr:(\S+)/&&say$1'

That looks like 28 characters for the simple version vs 36 (not 34) for the "winner".

September 30, 2008, 05:23

simpleton, just count the characters in the string.

ifconfig|perl -nE'/dr:(\S+)/&&say$1' is 34 characters not 36 as you suggest. Just 'echo -n ... | wc -c' it!

Your solution does not quite work. It does not list 127.0.0.1. Also, and IP can take 15 chars max (xxx.xxx.xxx.xxx), and you cut just 10...

simpleton Permalink
September 30, 2008, 19:10

Yep, you are right, mine won't work because of IP address length problems.

A corrected version using grep and cut is ~45 characters.

ifconfig|grep t\ a|cut -d" " -f12|cut -d: -f2

I still think you aren't counting right though. Please count the characters in:

ifconfig|perl -nE'/dr:(\S+)/&&say$1'

by hand, and you will get 36.

See:

[x@x ~]# ifconfig|perl -nE'/dr:(\S+)/&&say$1'
[x@x ~]# 123456789012345678901234567890123456

Compare:

[x@x ~]$ echo -n "ifconfig|perl -nE'/dr:(\S+)/&&say$1'" | wc -c
34
[x@x ~]$ echo -n "ifconfig|perl -nE'/dr:(\S+)/&&say'" | wc -c
34

"$1" is getting expanded to nothing, and you lose those characters in the count.

redondos Permalink
October 22, 2008, 19:08

What if your system has no free entries in its process table and you are *desperately* in need of your IPv4? This can happen… in some planets.

Here's a version for Linux without any fork()s, in the major shells that support string slicing/substrings. In some proud ~220 characters.

bash/ksh:

iface=eth0; while read Iface x x x x x x x x x x x x x SpecDst x; do [ "$Iface" = $iface ] && for i in 6 4 2 0; do echo -n $((0x${SpecDst:i:2})); ((i==0)) || echo -n .; done && echo && break; done < /proc/net/rt_cache

zsh:

iface=eth0; while read Iface x x x x x x x x x x x x x SpecDst x; do [ "$Iface" = $iface ] && for i in 7 5 3 1; do echo -n $((0x${SpecDst[i,i+1]})); ((i==1)) || echo -n .; done && echo && break; done < /proc/net/rt_cache

I wonder if this is easier to read from any other place than the routing cache.

Cheers.

suresh Permalink
December 04, 2008, 08:37

Try this

ifconfig -a|grep inet|cut -d" " -f2

suresh Permalink
December 04, 2008, 08:44

One more character less than above
ifconfig -a|grep ine|cut -d" " -f2

December 04, 2008, 08:45

suresh, it does not work:

$ /sbin/ifconfig -a|grep inet|cut -d' ' -f2


Even if you meant ':', it does not work:

$ /sbin/ifconfig -a|grep inet|cut -d':' -f2
192.168.1.2  Bcast
127.0.0.1  Mask
suresh Permalink
December 04, 2008, 08:55

Sorry I forget to put Double quotas
please correct it as follow

ifconfig -a|grep ine|cut -d" " -f2

suresh Permalink
December 04, 2008, 09:00

you can also use single quota instead of double quota as quoted by Mr.Peteris

December 26, 2008, 11:10

Looks like that's due to the differences in the output formats on different machines.

On Solaris, the following works - 33 chars:

ifconfig -a|awk '/inet/{print$2}'

or even the following - 32 chars:

ifconfig -a|awk '/ine/{print$2}'

Ben Permalink
February 08, 2009, 07:52

If you don't mind a little extra whitespace, here's a shorter one (I won't even use 5.10 :)

ifconfig|perl -00lne'print/dr:(\S+)/'
realbrew Permalink
February 20, 2009, 03:17

It's a little tougher to get a WAN address from a router. Here's the best I could do:

traceroute -m 1 -N 1 1.2|grep ms|perl -nle'/\((\S+)/&&print$1'|tr -d \)

Note the fake ip address "1.2" as argument to traceroute.
On another distro, one could leave out the -N 1 option to traceroute, but it's broken in Ubuntu Intrepid and won't do fewer than 6 hops without -N 1.

March 16, 2009, 18:58

remember GNU awk fields can be set using regex.

ifconfig | awk -F':| *' '/inet /{print $4}'

schemer Permalink
July 29, 2009, 17:05

bash on Ubuntu 8.04:

ip a|awk '/g/{print$2}'

23 characters
The 'g' matches in the word 'global', the scope value of the address, which is not on the line with the loopback address. As a bonus with this style, you also get the CIDR mask of the net.
Even better:

ipa(){ ip a|awk '/g/{print$2}';}

Then you can type 'ipa' anytime you want. Put it in .bashrc for persistence. Name it 'i' instead and you have a one character command to get your IP address.

July 31, 2009, 04:12

schemer, haha about 'i' :)

waldner Permalink
September 12, 2009, 20:37

The shortest solution using ip and awk:

ip a|awk '/ne/&&$0=$2'

Even shorter:

ip a|awk '/g/&&$0=$2'

These all fail miserably if you have "ne", "net" or "inet" in an interface's name, which is perfectly possible.

But back to ifconfig,

ifconfig|awk -F' |:' '/sk/&&$0=$13'
noah Permalink
November 13, 2009, 20:12

So how about getting the IPv6 address without the mask?

Leon King Permalink
November 24, 2009, 03:01

1. Can you write a short shell to find any IPV4 IP addresses in any files under /var/lib/*. Perform a dns reverse. Lookup for each IP found, and format the output neatly, like “IP=192.168.0.1, hostname=jo.blogg.com. http://jo.blog.com“?

suwone Permalink
January 19, 2010, 18:41
ip a|perl -pe '$_=/t (.*)\//?$1.$/:""'
shewfig Permalink
February 03, 2010, 00:53

hostname -i

11 characters, but only returns the IP address associated with the hostname.

wcoder Permalink
October 05, 2010, 00:31

ifconfig|awk '-F[ :]+' '/inet addr:/{print $4}'

if you want iface->IP:

ifconfig|awk '-F[ :]+' '/^[a-z]/{i=$1}/inet addr:/{print i,$4}'

wcoder Permalink
October 05, 2010, 00:55

ifconfig|awk '-F[ :]+' '/inet addr:/{print $4}'

if you want iface->IP:

ifconfig|awk '-F[ :]+' '/^[a-z]/{i=$1}/inet addr:/{print i,$4}'

October 10, 2010, 07:59

other form with grep and regex:

ip addr sh wlan0  | grep -E -o "([0-9]+.){3}[0-9]+" | head -1
abdula Permalink
September 10, 2011, 07:12

ifconfig | grep -E -o "([0-9]*\.){3}[0-9]"
ifconfig | grep -E -o "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*"
| grep -E -o "([0-9]{1,3}\.){3}[0-9]{1,3}" | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | uniq

October 03, 2011, 09:53

:D

time ifconfig eth0 | awk  '/inet adr:/ { print substr($2,5,length($2)) }'
#real   0m0.009s
#user   0m0.001s
#sys    0m0.010s
time ifconfig eth0 | grep 'inet adr:' | cut -d: -f2 | awk '{ print $1}'
#real   0m0.014s
#user   0m0.003s
#sys    0m0.017s
time TOTO=$(ifconfig eth0); TOTO=${TOTO##*adr\:}; TOTO=${TOTO%% *}
#real   0m0.007s
#user   0m0.002s
#sys    0m0.004s
time ifconfig eth0 | sed '/inet\ /!d;s/.*r://g;s/\ .*//g'
#real   0m0.008s
#user   0m0.001s
#sys    0m0.009s
time ip addr show eth0 | grep -Po '\d+\.\d+\.\d+\.\d+\/\d+' | cut -d '/' -f 1
#real   0m0.009s
#user   0m0.002s
#sys    0m0.011s
time ip addr show eth0 | awk '/inet/ { print substr($2,0,length($2)-3) }'
#real   0m0.007s
#user   0m0.001s
#sys    0m0.009s
time TOTO=$(ip addr show eth0); TOTO=${TOTO##*inet}; TOTO=${TOTO%%/*}; echo $TOTO
#real   0m0.005s
#user   0m0.002s
#sys    0m0.004s
time ifconfig eth0|perl -nE'/dr:(\S+)/&&say$1'
#real   0m0.013s
#user   0m0.005s
#sys    0m0.012s
time ifconfig eth0|perl -nE'/dr:(\S+)/&&say$1'
#real   0m0.013s
#user   0m0.005s
#sys    0m0.012s
time ip addr show eth0|perl -nE'/inet (\S+)\// && say $1'
#real   0m0.013s
#user   0m0.005s
#sys    0m0.011s
Amer EZAHIR Permalink
May 04, 2012, 10:06

#ifconfig eth0 | sed -n 2p | cut -d ":" -f2
172.168.1.100 Bcast

And to exclude the 'Bacast' word you add a second cut

#ifconfig eth0 | sed -n 2p | cut -d ":" -f2 | cut -d " " -f1
172.168.1.100

jpoole Permalink
August 13, 2012, 19:56

ifconfig -a | awk '/inet\ / { print $2 }'

puckie Permalink
March 01, 2014, 12:39

Inspired by waldner, i came to this:

ifconfig|awk '/ine/&&$0=$2'

At 27 characters. That

&&$0=

is a nice trick ;)

Leave a new comment

(why do I need your e-mail?)

(Your twitter name, if you have one. (I'm @pkrumins, btw.))

Type the word "cloud": (just to make sure you're a human)

Please preview the comment before submitting to make sure it's OK.

Advertisements