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
-
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
7 Answers
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

- 34,606
- 6
- 81
- 98
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
-
-
in broadcast and network functions there are two arguments. Shouldn't the bit `32 - $1` be `32 - $2` instead? I added the possibility of use cidr notation with something like `[ $# -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"` to be able to use it with the output of `ip -o -f inet addr show dev enp3s0 | awk '/scope global/ {print $4}'` – badc0de Feb 08 '22 at 12:03
-
-
Ah! I missed that. I have my own version of the script and at some point I must have changed that for clarity. Thanks anyway for your answer and code – badc0de Feb 09 '22 at 09:22
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)))"

- 101
- 1
- 3
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

- 23,083
- 6
- 50
- 70
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.

- 121
- 1
- 1
- 8
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}))

- 71
- 8
Based on https://stackoverflow.com/a/64877749/2716218 I tried to simplify and came up with
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

- 1,256
- 10
- 26