How to efficiently convert long int to dotted quad IP in bash [duplicate]
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'.
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"
}
See the INET_NTOA
function, you can use that to convert the number to an IP on the MySQL server.
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)))" }
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.
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.
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.
精彩评论