根据IP地址和子网掩码,如何使用bash计算网络地址?

18
在一个bash脚本中,我有一个形如192.168.1.15的IP地址和一个形如255.255.0.0的子网掩码。现在我想要计算这个网络的起始地址,也就是使用&运算符对这两个地址进行操作。在这个例子中,结果将会是192.168.0.0。是否有人已经准备好了类似于这样的东西?我正在寻找一种优雅的方法来处理bash中的IP地址。

2
您可以考虑使用ipcalc - dwalter
ipcalc是一个可以下载的Perl CGI脚本。谢谢,这对于任何想在PERL中实现它的人来说都是一个很好的资源。虽然我正在寻找bash的解决方案,但perl也可以在shell脚本中使用。 - Christian
1
不,我的意思是使用Unix命令行中的ipcalc计算子网。请参阅http://answers.oreilly.com/topic/411-how-to-calculate-subnets-with-ipcalc/获取更多信息。作为替代方案,您还可以使用支持IPv6的sipcalc(也是cli工具)。 - dwalter
如果我有足够的声望,我会将此添加为评论... cevings答案的用法... # 用法:子网掩码 #位数 # 例如,netmask 24 => 255.255.255.0 # 用法:广播地址掩码 # 例如,broadcast 192.168.0.1 24 => 192.168.0.255 # 用法:网络地址掩码 # 例如,network 192.168.0.123 24 => 192.168.0.0 - Andrew Sharpe
7个回答

45

使用按位&AND)运算符:

$ IFS=. read -r i1 i2 i3 i4 <<< "192.168.1.15"
$ IFS=. read -r m1 m2 m3 m4 <<< "255.255.0.0"
$ printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$((i2 & m2))" "$((i3 & m3))" "$((i4 & m4))"
192.168.0.0

使用另一个IP和掩码的示例:

$ IFS=. read -r i1 i2 i3 i4 <<< "10.0.14.97"
$ IFS=. read -r m1 m2 m3 m4 <<< "255.255.255.248"
$ printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$((i2 & m2))" "$((i3 & m3))" "$((i4 & m4))"
10.0.14.96

1
这太棒了。感谢您抽出时间来开发它。 - jacksonecac

16

一些总结所有其他答案的 Bash 函数。

ip2int()
{
    local a b c d
    { IFS=. read a b c d; } <<< $1
    echo $(((((((a << 8) | b) << 8) | c) << 8) | d))
}

int2ip()
{
    local ui32=$1; shift
    local ip n
    for n in 1 2 3 4; do
        ip=$((ui32 & 0xff))${ip:+.}$ip
        ui32=$((ui32 >> 8))
    done
    echo $ip
}

netmask()
# Example: netmask 24 => 255.255.255.0
{
    local mask=$((0xffffffff << (32 - $1))); shift
    int2ip $mask
}


broadcast()
# Example: broadcast 192.0.2.0 24 => 192.0.2.255
{
    local addr=$(ip2int $1); shift
    local mask=$((0xffffffff << (32 -$1))); shift
    int2ip $((addr | ~mask))
}

network()
# Example: network 192.0.2.0 24 => 192.0.2.0
{
    local addr=$(ip2int $1); shift
    local mask=$((0xffffffff << (32 -$1))); shift
    int2ip $((addr & mask))
}

6
很好,但如果有一个示例行,我就不用花费15分钟来弄清楚你的参数期望了。例如:“network 192.168.8.1 31”或“netmask 16”。 - A. Binzxxxxxx
那就这样吧,回到原来的方式。 - r2evans
在广播和网络函数中有两个参数。位 32 - $1 不应该改为 32 - $2 吗?我添加了使用 CIDR 表示法的可能性,例如 [ $# -eq 1 ] && echo "$1" | grep -E '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}' && IFS=/ read -ra cidr <<< "$1",以便能够将其与 ip -o -f inet addr show dev enp3s0 | awk '/scope global/ {print $4}' 的输出一起使用。 - badc0de
@badc0de 调用 shift 会将 $2 赋值给 $1 - ceving
啊!我错过了。我有自己的脚本版本,在某个时候为了清晰起见,我必须改变它。无论如何,感谢你的回答和代码。 - badc0de

10

如果您只有网络前缀(没有子网掩码),可以添加一种替代方法:

IP=10.20.30.240
PREFIX=26
IFS=. read -r i1 i2 i3 i4 <<< $IP
IFS=. read -r xx m1 m2 m3 m4 <<< $(for a in $(seq 1 32); do if [ $(((a - 1) % 8)) -eq 0 ]; then echo -n .; fi; if [ $a -le $PREFIX ]; then echo -n 1; else echo -n 0; fi; done)
printf "%d.%d.%d.%d\n" "$((i1 & (2#$m1)))" "$((i2 & (2#$m2)))" "$((i3 & (2#$m3)))" "$((i4 & (2#$m4)))"

3

针对那些在谷歌搜索时遇到此问题,并需要提供适用于ash,即包含在BusyBox中和许多路由器上的sh的答案,这里有一个解决方案:

IP=10.20.30.240
MASK=255.255.252.0
IFS=. read -r i1 i2 i3 i4 << EOF
$IP
EOF
IFS=. read -r m1 m2 m3 m4 << EOF
$MASK
EOF
read masked << EOF
$(( $i1 & $m1 )).$(( $i2 & $m2 )).$(( $i3 & $m3 )).$(( $i4 & $m4 ))
EOF
echo $masked

如果你只有前缀长度,该怎么办:

IP=10.20.30.240
PREFIX=22
IFS=. read -r i1 i2 i3 i4 << EOF
$IP
EOF
mask=$(( ((1<<32)-1) & (((1<<32)-1) << (32 - $PREFIX)) ))
read masked << EOF
$(( $i1 & ($mask>>24) )).$(( $i2 & ($mask>>16) )).$(( $i3 & ($mask>>8) )).$(( $i4 & $mask ))
EOF
echo $masked

1
很好的答案,上面的回答有一个小错字。
$ printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$(($i2  <-- $i2 should be i2

如果有人知道如何计算广播地址(对网络进行XOR运算),然后计算网络和广播之间可用节点的数量,我会对下一步感兴趣。 我需要在/23中的列表中查找地址。

哪个答案在上面?有几个答案。 - NiKiZe

0
除了@Janci的回答之外
IP=10.20.30.240
PREFIX=26
IFS=. read -r i1 i2 i3 i4 <<< $IP
D2B=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})
binIP=${D2B[$i1]}${D2B[$i2]}${D2B[$i3]}${D2B[$i4]}
binIP0=${binIP::$PREFIX}$(printf '0%.0s' $(seq 1 $((32-$PREFIX))))
# binIP1=${binIP::$PREFIX}$(printf '0%.0s' $(seq 1 $((31-$PREFIX))))1
echo $((2#${binIP0::8})).$((2#${binIP0:8:8})).$((2#${binIP0:16:8})).$((2#${binIP0:24:8}))

0

基于https://dev59.com/72Up5IYBdhLWcg3wNVhb#64877749,我尝试简化并得出了以下结果

IP=10.20.30.240
PREFIX=22
IFS=. read -r i1 i2 i3 i4 <<< $IP
mask=$(( ((1<<32)-1) & (((1<<32)-1) << (32 - $PREFIX)) ))
masked=$(( $i1 & ($mask>>24) )).$(( $i2 & ($mask>>16) )).$(( $i3 & ($mask>>8) )).$(( $i4 & $mask ))
echo $masked

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