In a bash script I have an IP address like 192.168.1.15 and a netmask like 255.255.0.0. I now want to calculate the start address of this network, that means using the &-operator on both addresses. In the example, the result would be 192.168.0.0. Does someone have something like this ready? I'm looking for an elegant way to deal with ip addresses from bash
Asked
Active
Viewed 3.3k times
17
-
2You may consider `ipcalc`. – dwalter Mar 15 '13 at 10:24
-
ipcalc is a perl cgi script that can be downloaded. Thanks, that is a great source for anyone who wants to implement it in PERL. I was looking for a solution in bash though, but perl would work in a shell script, too. – Christian Mar 15 '13 at 10:36
-
1no I mean the unix command line to ipcalc. see http://answers.oreilly.com/topic/411-how-to-calculate-subnets-with-ipcalc/ for more information. As an alternative you could also use sipcalc (also a cli tool) which supports IPv6 – dwalter Mar 15 '13 at 10:37
-
I would have added this as a comment if I had the rep... Usage for cevings answer... # usage: netmask #bits # eg. netmask 24 => 255.255.255.0 # usage: broadcast address mask # eg. broadcast 192.168.0.1 24 => 192.168.0.255 # usage: network address mask # eg. network 192.168.0.123 24 => 192.168.0.0 – Andrew Sharpe Apr 23 '18 at 04:25
6 Answers
43
Use bitwise &
(AND
) operator:
$ 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
Example with another IP and mask:
$ 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
kamituel
- 30,600
- 3
- 71
- 91
14
Some Bash functions summarizing all other answers.
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))
}
-
6Nice but an example line would have saved me 15mins of figuring out your parameter expectations.. "network 192.168.8.1 31" or "netmask 16" – A. Binzxxxxxx May 11 '16 at 17:07
-
6
Just adding an alternative if you have only network prefix available (no netmask):
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)))"
Janci
- 61
- 1
- 2
2
Great answer, though minor typo in answer above.
$ printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$(($i2 <-- $i2 should be i2
If anyone knows how to calculate the broadcast address (XOR the network), then calculate the usable nodes between network and broadcast I'd be interested in those next steps. I have to find addresses in a list within a /23.
user3126740
- 113
- 1
- 8
1
For people who hit this while googling and need an answer that works in ash
, the sh
that's included in BusyBox and therefore on many routers, here's something for that case:
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
And here's what to do if you only have the prefix length:
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
Daniel Martin
- 21,725
- 6
- 46
- 64
0
In addition to @Janci answer
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}))
Mikhail
- 71
- 8