7

I have a lot of IP addresses in a large mysql DB stored as long int's. I need an efficient/quick way to convert them back to an IP within a BASH shell script ( or have mysql return the results as an IP?? ).

Note: specifically don't want to call perl, awk, or other 'language'.

Mark
  • 73
  • 1
  • 1
  • 4

6 Answers6

15

Since you asked for Bash:

INET_NTOA() { 
    local IFS=. num quad ip e
    num=$1
    for e in 3 2 1
    do
        (( quad = 256 ** e))
        (( ip[3-e] = num / quad ))
        (( num = num % quad ))
    done
    ip[3]=$num
    echo "${ip[*]}"
}

INET_ATON ()
{
    local IFS=. ip num e
    ip=($1)
    for e in 3 2 1
    do
        (( num += ip[3-e] * 256 ** e ))
    done
    (( num += ip[3] ))
    echo "$num"
}

Examples:

$ INET_ATON 10.2.1.255
167903743
$ INET_NTOA 167903743
10.2.1.255

Here is a version that will work in any of the Bourne-derived shells I tried including dash, ksh, several versions of Bash, BusyBox ash, zsh (with -y) and even the Heirloom Bourne Shell.

INET_NTOA() {
    num=$1
    ip=
    for e in 3 2 1
    do
        quad=`echo "256 ^ $e" | bc`
        if [ -n "$ip" ]
        then
            ip=$ip.
        fi
        ip=$ip`echo "$num / $quad" | bc`
        num=`echo "$num % $quad" | bc`
    done
    ip=$ip.$num
    echo "$ip"
}

INET_ATON ()
{
    num=0
    e=3
    saveIFS=$IFS
    IFS=.
    set -- $1
    IFS=$saveIFS
    for ip in "$@"
    do
        num=`echo "$num + $ip * 256 ^ $e" | bc`
        e=`echo "$e - 1" | bc`
    done
    echo "$num"
}
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • 1
    +1, thanks for these functions! If you attempt to do masking with large integers, you can run into wraparound problems. Applying the workaround in [this answer](http://stackoverflow.com/questions/210629/python-unsigned-32-bit-bitwise-arithmetic) fixes that problem. E.g. `INET_NTOA $(( ($(INET_ATON 192.168.1.12) | ~$(INET_ATON 255.255.255.0)) ))` yields `-63.-87.-254.-1`, but `INET_NTOA $(( ($(INET_ATON 192.168.1.12) | ~$(INET_ATON 255.255.255.0)) & 0xFFFFFFFF ))` yields `192.168.1.255` which is what you want. – bstpierre Mar 02 '11 at 21:51
  • When I tried to run this in shell it didn't work, it has problem with the following lines: `echo "${ip[*]}"` and `ip=($1)` – ArmenB Sep 06 '13 at 02:35
  • @Armen: Which shell are you using and what kind of problem? Please post a specific error message. It's likely that you're using a shell which doesn't support arrays or uses a different syntax. – Dennis Williamson Sep 06 '13 at 06:24
  • @Dennis: That is correct I'm not using Bourne Again Shell, im using vanilla Shell. The error message says "expecting } before [" and similar. – ArmenB Sep 06 '13 at 16:04
  • @ArmenB.: I added a version that should work in your shell. – Dennis Williamson Mar 18 '14 at 16:56
  • Excellent use of pure arithmetics in bash! I approve :-) – Alexander Janssen Feb 04 '16 at 09:24
11

See the INET_NTOA function, you can use that to convert the number to an IP on the MySQL server.

Lukáš Lalinský
  • 40,587
  • 6
  • 104
  • 126
3
INET_ATON()
  {
  local IFS=. ipStr

  ipStr=($1)
  echo $(($(($(($(($(($((${ipStr[0]} * 256)) + ${ipStr[1]})) * 256)) + ${ipStr[2]})) * 256)) + ${ipStr[3]}))
  }

INET_NTOA()
  {
  echo "$(($1 / 16777216)).$(($(($1 % 16777216)) / 65536)).$(($(($1 % 65536)) / 256)).$(($1 % 256))"
  }

subnetRange()
  {
  local addr=$(INET_ATON "$1") mask=$(INET_ATON "$2")

  echo "$(INET_NTOA $(($addr & $mask))) $(INET_NTOA $(($addr | $mask ^ 4294967295)))"
  }


1

Given that the original question was about an efficient way to do this in bash, I think these might be worthy entries in the contest:

INET_ATON()
  {
  local IFS=. ip
  ip=($*)
  echo $(((ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3]))
  }

INET_NTOA()
  {
  echo $(($1 >> 24)).$((($1 >> 16) % 256)).$((($1 >> 8) % 256)).$(($1 % 256))
  }

Naturally, these have the same "wrapping" problem as the other solutions given here, due to the shell's treatment of integers as signed.

barush
  • 303
  • 2
  • 6
1

Suppose you have a field called ip_addr in a table called hosts

Then

select INET_NTOA(ip_addr) from hosts;

Would do the query and return dotted quad IPs in the result set.

Jesse Dhillon
  • 7,841
  • 1
  • 34
  • 34
0

i'm lazy, and this has already been done. So from a shell

$ ping -c1 1199092913 | head -n1 | grep -Eow "[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+"

i'm sure there are other clever ways to do it from shell as well.

Bucket
  • 7,415
  • 9
  • 35
  • 45