IP地址转换器

13

这里有一个IP地址:66.102.13.19,从这个地址中接收到了该地址。

http://1113984275

但是怎么做呢?我如何借助bash实现这个功能。例如,这个服务可以实现,但我不理解算法。

7个回答

39
ip=66.102.13.19
IFS=. read -r a b c d <<< "$ip"
printf '%s%d\n' "http://" "$((a * 256 ** 3 + b * 256 ** 2 + c * 256 + d))"

顺便提一下,你问题中的数字与IP地址不匹配。

将十进制转换为IP地址:

#!/bin/bash
dec2ip () {
    local ip delim dec=$@
    for e in {3..0}
    do
        ((octet = dec / (256 ** e) ))
        ((dec -= octet * 256 ** e))
        ip+=$delim$octet
        delim=.
    done
    printf '%s\n' "$ip"
}

dec2ip "$@"

将IP地址转换为十进制数:

#!/bin/bash
ip2dec () {
    local a b c d ip=$@
    IFS=. read -r a b c d <<< "$ip"
    printf '%d\n' "$((a * 256 ** 3 + b * 256 ** 2 + c * 256 + d))"
}

ip2dec "$@"

演示:

$ ./dec2ip 1113984275
66.102.13.19

$ ./ip2dec 66.102.13.19
1113984275

这两个脚本依赖于Bash中一些Bourne派生Shell中不存在的功能。下面是可以使用的AWK版本:

#!/usr/bin/awk -f
# dec2ip
BEGIN {
    dec = ARGV[1]
    for (e = 3; e >= 0; e--) {
        octet = int(dec / (256 ^ e))
        dec -= octet * 256 ^ e
        ip = ip delim octet
        delim = "."
    }
    printf("%s\n", ip)
}

并且

#!/usr/bin/awk -f
# ip2dec
BEGIN {
    ip = ARGV[1]
    split(ip, octets, ".")
    for (i = 1; i <= 4; i++) {
        dec += octets[i] * 256 ** (4 - i)
    }
    printf("%i\n", dec)
}

它们可以像上面的Bash脚本一样调用。


@ArmenB.:我不确定你在问什么,但通常可以在shell脚本中完成的任何事情都可以在命令行中完成,反之亦然。有一些例外或必须以稍微不同的方式完成的情况。在这种情况下,您可以将函数(只是函数定义省略了shebang和调用函数)包含在文件中,例如~/bin/functions,并从您的~/.bashrc中源该文件使用. $HOME/bin/functions(注意句点)。然后,您可以像这样从命令行调用函数:例如ip2dec 66.102.13.19 - Dennis Williamson
如果你指的是除了Bash之外的其他shell,那么是的,但是如果其他shell没有使用这里所用的Bash相同的功能,则可能需要重写。 - Dennis Williamson
@Dennis:是的,我指的是其他 shell,而不是 bash。我正在寻找一种在原始 shell 中实现此操作的方法。 - ArmenB
@ArmenB.:不幸的是,Bourne shell不支持数组(尽管您可以使用set和位置参数),它和expr都不支持指数运算。此外,不同版本的Bourne衍生shell支持(或不支持)各种其他功能。因此,为了获得可用的东西,我添加了AWK版本。希望它们能与您正在使用的AWK版本兼容。 - Dennis Williamson
注意将IP转换为十进制时,在循环之前初始化十进制值,否则它会继续增加 :) - shacharsol
显示剩余13条评论

15

IP地址 -> 数字:

echo 66.102.13.19 | tr . '\n' | awk '{s = s*256 + $1} END{print s}'

数字 -> IP地址:

(export ip=1113984275; for i in {1..4}; do s='.'$((ip%256))$s && ((ip>>=8)); done; echo ${s:1})

太好了,但我们如何以另一种方式实现呢?即 IP 数字 → IP 地址。 - bsmoo
1
我已经更新了将IP数字转换为IP地址的方法。 :) - Hongbo Liu
2
你可以通过指定记录分隔符来避免调用 tr,例如:awk 'BEGIN{RS="."}... 或者 awk '{s = s*256 + $1} END{print s}' RS=. - bufh

4

二进制移位比乘法或除法更快。
使用二进制 AND 比取模更快。

ip2dec(){ # Convert an IPv4 IP number to its decimal equivalent.
          declare -i a b c d;
          IFS=. read a b c d <<<"$*";
          echo "$(((a<<24)+(b<<16)+(c<<8)+d))";
        }
dec2ip(){ # Convert an IPv4 decimal IP value to an IPv4 IP.
          declare -i a=$((0xff)) b=$1; 
          c="$((b>>24&a)).$((b>>16&a)).$((b>>8&a)).$((b&a))";
          echo "$c";
        }

ip=66.102.13.19
a=$(ip2dec "$ip")
b=$(dec2ip "$a")
echo "$ip DecIP=$a IPv4=$b "

注意:这个系统会在遇到像0008这样的值时失败。Bash会认为它是一个八进制数。
为了解决这个问题,每个值都需要使用类似以下的方法进行清除:
IsDecInt()(     # Is the value given on $1 a decimal integer (with sign)?
                declare n=$1; set --
                if [[ $n =~ ^([+-]?)((0)|0*([0-9]*))$ ]]; then
                    set -- "${BASH_REMATCH[@]:1}"
                else
                    exit 1
                fi
                echo "$1$3$4";
          )

  a=$(IsDecInt "$a")||{ Echo "Value $a is not an integer" >&2; exit 1; }

对于(非常)老的shell:从bash 2.04开始,或者dash或任何(合理的)shell:

ip2dec(){ # Convert an IPv4 IP number to its decimal equivalent.
          local a b c d;
          IFS=. read a b c d <<-_EOF_
$1
_EOF_
          echo "$(((a<<24)+(b<<16)+(c<<8)+d))";
        }

dec2ip(){   # Convert an IPv4 decimal IP value to an IPv4 IP.
            local a=$((~(-1<<8))) b=$1; 
            c="$((b>>24&a)).$((b>>16&a)).$((b>>8&a)).$((b&a))";
            echo "$c";
        }

ip=$1
a=$(ip2dec "$ip")
b=$(dec2ip "$a")
echo "$ip DecIP=$a IPv4=$b "

要清理变量abcd中的前导0并防止八进制解释,您可以执行以下操作:((a=10#$a,b=10#$b,c=10#$c,d=10#$d))。参见:https://dev59.com/emgu5IYBdhLWcg3wuJSF 或者不使用declare -i而是使用echo "$(((10#$a<<24)+(10#$b<<16)+(10#$c<<8)+10#$d))"; - Léa Gris

3
这是我的看法:
$ host google.com
google.com has address 216.58.216.142
$ ./ip2d.sh 216.58.216.142
3627735182
$ ./d2ip.sh 3627735182
216.58.216.142

ip2d.sh:

#!/bin/bash

IFS=.
set -- $*
echo $(( ($1*256**3) + ($2*256**2) + ($3*256) + ($4) ))

d2ip.sh:

#!/bin/bash

IFS=" " read -r a b c d  <<< $(echo  "obase=256 ; $1" |bc)
echo ${a#0}.${b#0}.${c#0}.${d#0}

喜欢它。ip2d可以作为(bash)函数进行编写,如ip2d() { IFS=. eval 'set -- $1'; echo ...},而d2ip也可以这样写:IFS=\ read -r a b c d < <(echo "obase=256;$1" |bc) - bufh
再短一点:A=($(dc -e "256o$1p"));IFS=. eval 'echo "${A[*]#0}"'(当四位数有两个前导零时,与原始问题相同)。 - bufh

3

我对整数转IP地址的版本:

echo 3232235521| awk {'print rshift(and($1, 0xFF000000), 24) "." rshift(and($1, 0x00FF0000), 16) "." rshift(and($1, 0x0000FF00), 8) "." and($1, 0x000000FF) '}

注意:以上代码为HTML标签,不要删除。


2

针对bash的简单整数到IP地址的转换

dec2ip ()
{
   local v=$1
   local i1=$((v>>24&255))
   local i2=$((v>>16&255))
   local i3=$((v>>8&255))
   local i4=$((v&255))
   printf '%d.%d.%d.%d\n' $i1 $i2 $i3 $i4
}

2
我认为有一个更简单的解决方案,可以处理任意数量的八位字节,而且不需要引用固定偏移量等。
echo 66.102.13.19 |
    tr . '\n' |
    while read octet; do
        printf "%.08d" $(echo "obase=2;$octet" | bc)
    done |
    echo $((2#$(cat)))

输出:1113984275


你好,我对你的回答有一个问题。你能解释一下 $((2#$(cat))) 的含义吗? - bwangel
1
@yundongxu cat 命令读取从标准输入中传输的数据,该数据是一串二进制数字。表达式 $((2#...)) 将 "..." 从二进制转换为十进制。 - Eric Pruitt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接