Increment IP address in a shell script - bash

It is possible to write this script in another way to increment ip address whitout loops like this? Script, a port scanner, works fine and mine is only a question about efficiency. Sorry for my english and thanks for your answers.
#!/bin/bash
ip=$1
IFS=. read i1 i2 i3 i4 <<< "$ip"
port=$2
max=255
while [ $i1 -le $max ];do
while [ $i2 -le $max ]; do
while [ $i3 -le $max ]; do
while [ $i4 -le $max ]; do
timeout 0.4 bash -c "echo >/dev/tcp/$i1.$i2.$i3.$i4/$port" && echo "on $i1.$i2.$i3.$i4 port $port is open"
i4=$(($i4+1))
done
i4=0
i3=$(($i3+1))
done
i3=0
i2=$(($i2+1))
done
i2=0
i1=$(($i1+1))
done

This function just prints next ip:
nextip(){
IP=$1
IP_HEX=$(printf '%.2X%.2X%.2X%.2X\n' `echo $IP | sed -e 's/\./ /g'`)
NEXT_IP_HEX=$(printf %.8X `echo $(( 0x$IP_HEX + 1 ))`)
NEXT_IP=$(printf '%d.%d.%d.%d\n' `echo $NEXT_IP_HEX | sed -r 's/(..)/0x\1 /g'`)
echo "$NEXT_IP"
}
So you can increment it like:
FIRST_IP=192.168.1.250
NUM=10
IP=$FIRST_IP
for i in $(seq 1 $NUM); do
echo $IP
IP=$(nextip $IP)
done
Sample output:
192.168.1.250
192.168.1.251
192.168.1.252
192.168.1.253
192.168.1.254
192.168.1.255
192.168.2.0
192.168.2.1
192.168.2.2
192.168.2.3

Take a look at:
i1=0; i2=0; i3=0; i4=0
max=255
eval printf -v ip "%s\ " {$i1..$max}.{$i2..$max}.{$i3..$max}.{$i4..$max}
for i in $ip; do
# do here somthing with $i
done

Related

Ip host script bash

In the first if we want the hostname to appear, which is the 5th field from a file. Then if the IP we give to the host command does not exist, then the command returns message 3 (NXDOMAIN). The script should recognize if the command was "not found". In this case it will
must simply print (-).
#!/bin/bash
ip="$1"
if [ "$ip" ] ; then
host "$ip" | cut -d' ' -f5
elif
[[ "$ip" =~ "[3(NXDOMAIN)]$" ]] ; then
echo "-"
fi
Do u have any solution on this exercise?
You're not testing the result of the host command, you're testing the value of the original $ip variable.
Save the output to a variable, test that variable, then either print the output or - depending on the test.
You don't need to do a regexp match, just match the exact string.
#!/bin/bash
ip="$1"
if [ "$ip" ] ; then
result=$(host "$ip" | cut -d" " -f5)
if [[ $result = "3(NXDOMAIN)" ]] ; then
echo "-"
else
echo "$result"
fi
fi
The answer is much simpler than you think, you don't need to do any matching. You can just use the return code from host
#!/bin/bash
ip="$1"
if domain=$(host "$1"); then
echo "${domain##* }"
else
echo "-"
fi
Proof of Concept
$ testHost(){ if domain=$(host "$1"); then echo "${domain##* }"; else echo "-"; fi }
$ testHost 172.217.6.46
sfo03s08-in-f14.1e100.net.
$ testHost 172.217.6.466
-
#!/bin/bash
if [ -n "$1" ] && [[ $1 =~ ^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$ ]] ;then
res=$(host "$1" | cut -d' ' -f5)
if [ "$res" != "3(NXDOMAIN)" ]; then
echo "$res"
else
echo "-"
fi
else
echo "please enter a valid ip"
fi
if you want to cover also ipv6 then I think this will cover it
#!/bin/bash
# ipv4
if [[ $1 =~ ^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])\.([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])\.([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])\.([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])$ ]]; then
res=`host "$1" | cut -d' ' -f5`
if [ "$res" != "3(NXDOMAIN)" ]; then
echo "$res"
else
# valid ipv4 IP but not connected
echo "-"
fi
# ipv6
elif [[ $1 =~ ^(([[:xdigit:]]{1,4}:){7,7}[[:xdigit:]]{1,4}|([[:xdigit:]]{1,4}:){1,7}:|([[:xdigit:]]{1,4}:){1,6}:[[:xdigit:]]{1,4}|([[:xdigit:]]{1,4}:){1,5}(:[[:xdigit:]]{1,4}){1,2}|([[:xdigit:]]{1,4}:){1,4}(:[[:xdigit:]]{1,4}){1,3}|([[:xdigit:]]{1,4}:){1,3}(:[[:xdigit:]]{1,4}){1,4}|([[:xdigit:]]{1,4}:){1,2}(:[[:xdigit:]]{1,4}){1,5}|[[:xdigit:]]{1,4}:((:[[:xdigit:]]{1,4}){1,6})|:((:[[:xdigit:]]{1,4}){1,7}|:)|fe80:(:[[:xdigit:]]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[[:digit:]]){0,1}[[:digit:]])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[[:digit:]]){0,1}[[:digit:]])|([[:xdigit:]]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[[:digit:]]){0,1}[[:digit:]])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[[:digit:]]){0,1}[[:digit:]]))$ ]]; then
res=`host "$1" | cut -d' ' -f5`
if [ "$res" != "3(NXDOMAIN)" ]; then
echo "1. $res"
else
# valid ipv6 IP but not connected
echo "2. -"
fi
else
echo "Please enter a valid IP"
fi
Note: For some versions of bash the -4 and -6 options do not work.
Thanks to Léa Gris for pointing out the locales problem.
Inspired from https://helloacm.com/how-to-valid-ipv6-addresses-using-bash-and-regex/

Ping a host and output in CSV format in bash

I have a CSV file named ip.csv in the following format:
Host,Status
192.168.0.2
192.168.0.4
I want to ping IP and save output in the same file, ip.csv, next to the host. Output should look like:
Host,Status
192.168.0.2,Alive
192.168.0.4,Down
When I executed below script, I didn't get desired output:
#!/bin/bash
while IFS=',' read -r host status
do
ping -c 1 "$host"
if [ $? -eq 0 ]; then
echo -e "$host,Alive" >> ip.csv
else
echo -e "$host,Down" >> ip.csv
fi
done < ip.csv
Please suggest.
you can do it using this changes -
#!/bin/bash
a=""
while IFS=',' read -r host status
do
if [ "$host" = "Host" ]; then
a="${a}${host}"','"${status}"$'\n'
else
ping -c 1 "$host"
if [ $? -eq 0 ]; then
a="${a}${host}"',Alive'$'\n'
else
a="${a}${host}"',Down'$'\n'
fi
fi
done < ip.csv
echo -e "${a}" > ip.csv

IP address calculator script

I need to write a Bash script that converts IP address from CIDR format to quad-style.
I must enter the IP address in the following style:
10.10.10.10/24
If I entered it in this style:
10.10.10.10 255.255.255.0
an error message should appear.
I tried this script:
#!/bin/bash
echo "enter you ip"
read ip
case $ip in
*.*.*.*/*)
b=`echo $ip | cut -d/ -f1`
a=`echo $ip | cut -d/ -f2`
if [ $a -eq 24 ];then
echo "$b 255.255.255.0"
elif [ $a -eq 25 ];then
echo "$b 255.255.255.128"
elif [ $a -eq 26 ];then
echo "$b 255.255.255.192"
elif [ $a -eq 27 ];then
echo "$b 255.255.255.224"
elif [ $a -eq 28 ];then
echo "$b 255.255.255.240"
elif [ $a -eq 29 ];then
echo "$b 255.255.255.248"
elif [ $a -eq 30 ];then
echo "$b 255.255.255.252"
elif [ $a -eq 31 ];then
echo "$b 255.255.255.254"
elif [ $a -eq 32 ];then
echo "$b 255.255.255.255"
fi
case $ip in
*.*.*.* *.*.*.*)
echo "enter a valid address"
esac
but I get an error
./ipcalculater2.sh: line 32: syntax error near unexpected token `...'
./ipcalculater2.sh: line 32: ` ... ...)'
What is wrong with my script?
Here's an example of four ways one might convert from CIDR to netmask notation in bash.
#!/usr/bin/env bash
if [[ "$1" != *.*.*.*/* ]]; then
echo "Usage: ${0##*/} ip.ad.dr.ess/bits" >&2
exit 1
fi
# separate our input into network and mask using IFS
IFS=/ read network bits <<<"$1"
# build a temporary variable $s that we'll use to build $bitmask
# for the first three variants...
read zeros <<< $(printf '%032s' 0)
s=${zeros//0/1}${zeros}
# convert the mask into a 32-digit binary number
bitmask=${s:$((32-bits)):32}
# Four different methods for generating the netmask...
# The first two use `bc` and `dc`. One is likely installed on your system.
read mask1 <<<$( dc -e "2i $(fold -w8 <<<"$bitmask " | paste -sdp -)" | paste -sd. - )
read mask2 <<<$( fold -w8 <<<"$bitmask" | paste - | bc -e 'ibase=2' | paste -sd. - )
# And if dc and bc are unavailable, or you prefer not to spawn subshells, or
# risk missed dependencies, you can do this in pure bash with a few more lines.
unset mask3
for ((i=0;i<4;i++)); do
mask3+="${mask3:+.}$((2#${bitmask:$((8*i)):8}))"
done
# And finally, instead of a loop, you can do the same thing with fancy math:
# This variant avoides the need for $bitmask, set above.
mask4="$(( 2**32 - (1 << (32-$bits)) ))"
mask4=$(( mask4/2**24 )).$(( mask4/2**16 %256 )).$(( mask4/2**8 % 256 )).$(( mask4 % 256 ))
# Print the results, obviously.
echo "network=$network"
echo "netlength=$bits"
echo "netmask via 'dc': $mask1"
echo "netmask via 'bc': $mask2"
echo "netmask via loop: $mask3"
echo "netmask via math: $mask4"
I've included code that works in each of dc and bc, since I can't predict which calculator will be available on your system. These calculators are used for base conversion. If you don't mind your script being a little longer, you can avoid spawning external tools (like fold and paste and the calculator) using the method that generates $netmask3.
Note that in the third case, this usage of bc depends on availability of a -e option which exists in *BSD. Use another variant if you're using GNU bc (i.e. you're in Linux.)
You can of course adjust the output so that it meets your needs, as well as trim the parts of the script you're not using.
Please check for this corrections:
#!/bin/bash
echo "enter you ip"
read ip
case $ip in
*.*.*.*/*)
b=`echo $ip | cut -d/ -f1`
a=`echo $ip | cut -d/ -f2`
if [ $a -eq 24 ];then
echo "$b 255.255.255.0"
elif [ $a -eq 25 ];then
echo "$b 255.255.255.128"
elif [ $a -eq 26 ];then
echo "$b 255.255.255.192"
elif [ $a -eq 27 ];then
echo "$b 255.255.255.224"
elif [ $a -eq 28 ];then
echo "$b 255.255.255.240"
elif [ $a -eq 29 ];then
echo "$b 255.255.255.248"
elif [ $a -eq 30 ];then
echo "$b 255.255.255.252"
elif [ $a -eq 31 ];then
echo "$b 255.255.255.254"
elif [ $a -eq 32 ];then
echo "$b 255.255.255.255"
fi
;;
*.*.*.*\ *.*.*.*)
echo "enter a valid address"
;;
esac

Utilizing bash to multiply an interger by a float with an if statement

Utilizing bash to multiply an interger by a float with an if statement, I will further expand on this with two other cases outside 0.90, just trying to learn the basics.
#!/bin/bash
echo -n "give me an integer: [ENTER]: "
read that_integer
if -n [ $that_integer <= 5000 ]; then
awk print $that_integer*0.90
done
Ultimately, this is the code I came up with.
Thanks!
#!/bin/bash
echo "give me an integer: [ENTER]: "
read that_integer
if [ $that_integer -le 5000 ]; then
awk -v x=$that_integer 'BEGIN { print x*0.90 }' < /dev/null
elif [ $that_integer -gt 5000 -a $that_integer -le 50000 ]; then
awk -v x=$that_integer 'BEGIN { print x*0.70 }' < /dev/null
else
awk -v x=$that_integer 'BEGIN { print x*0.40 }' < /dev/null
fi
done
You can use the bc utility to do floating point arithmetic in the shell.
echo "give me an integer: [ENTER]: "
read that_integer
if [ $that_integer -le 5000 ]; then
echo "$that_integer*0.90"|bc
elif [ $that_integer -gt 5000 -a $that_integer -le 50000 ]; then
echo "$that_integer*0.70"|bc
else
echo "$that_integer*0.40"|bc
fi
The -n does nothing; remove it.
For integer comparisons, use -le:
if [ $that_integer -le 5000 ]; then
The correct syntax for the awk call is
awk -v x=$that_integer 'BEGIN { print x*0.90 }' < /dev/null
if statements are terminated with fi, not done.
As you know, bash can't handle floats, but you can re-cast this to use integer maths:
x=$((5000*90/100))
echo $x
4500
So, just for kicks:
#!/bin/bash
echo "give me an integer: [ENTER]: "
read that_integer
multiplier=40
[ $that_integer -le 5000 ] && multiplier=90
[ $that_integer -gt 5000 -a $that_integer -le 50000 ] && multiplier=70
echo $(($that_integer*$multiplier/100))

Bash script to list all IPs in prefix

I'm trying to create script that I can input a set of prefixes, which will then list all IP addresses within the prefixes (including network/host/broadcast).
An example would be:
./convert-prefix-to-IPs.sh 192.168.0.0/23 203.20.0.0/16
192.168.0.0
192.168.0.1
...
192.168.0.255
192.168.1.0
..
192.168.1.255
203.20.0.0
..
203.20.255.255
There are some python/perl scripts which can do this, but I'm hoping to have a simple bash script, as it may be used on systems without perl/python (yes.. i know.. )
Here is what I use to generate all the IP addresses in a given CIDR block
nmap -sL -n 10.10.64.0/27 | awk '/Nmap scan report/{print $NF}'
From the nmap man page, the flags are:
-sL: List Scan - simply list targets to scan
-n: Never do DNS resolution
Just that simple
The above command outputs this
10.10.64.0
10.10.64.1
10.10.64.2
10.10.64.3
10.10.64.4
10.10.64.5
10.10.64.6
10.10.64.7
10.10.64.8
10.10.64.9
10.10.64.10
10.10.64.11
10.10.64.12
10.10.64.13
10.10.64.14
10.10.64.15
10.10.64.16
10.10.64.17
10.10.64.18
10.10.64.19
10.10.64.20
10.10.64.21
10.10.64.22
10.10.64.23
10.10.64.24
10.10.64.25
10.10.64.26
10.10.64.27
10.10.64.28
10.10.64.29
10.10.64.30
10.10.64.31
I too was looking for this solution and found that #scherand script worked great. I also have added to this script to give you more option. Help File below.
THIS SCRIPT WILL EXPAND A CIDR ADDRESS.
SYNOPSIS
./cidr-to-ip.sh [OPTION(only one)] [STRING/FILENAME]
DESCRIPTION
-h Displays this help screen
-f Forces a check for network boundary when given a STRING(s)
-i Will read from an Input file (file should contain one CIDR per line) (no network boundary check)
-b Will do the same as –i but with network boundary check
EXAMPLES
./cidr-to-ip.sh 192.168.0.1/24
./cidr-to-ip.sh 192.168.0.1/24 10.10.0.0/28
./cidr-to-ip.sh -f 192.168.0.0/16
./cidr-to-ip.sh -i inputfile.txt
./cidr-to-ip.sh -b inputfile.txt
#!/bin/bash
############################
## Methods
############################
prefix_to_bit_netmask() {
prefix=$1;
shift=$(( 32 - prefix ));
bitmask=""
for (( i=0; i < 32; i++ )); do
num=0
if [ $i -lt $prefix ]; then
num=1
fi
space=
if [ $(( i % 8 )) -eq 0 ]; then
space=" ";
fi
bitmask="${bitmask}${space}${num}"
done
echo $bitmask
}
bit_netmask_to_wildcard_netmask() {
bitmask=$1;
wildcard_mask=
for octet in $bitmask; do
wildcard_mask="${wildcard_mask} $(( 255 - 2#$octet ))"
done
echo $wildcard_mask;
}
check_net_boundary() {
net=$1;
wildcard_mask=$2;
is_correct=1;
for (( i = 1; i <= 4; i++ )); do
net_octet=$(echo $net | cut -d '.' -f $i)
mask_octet=$(echo $wildcard_mask | cut -d ' ' -f $i)
if [ $mask_octet -gt 0 ]; then
if [ $(( $net_octet&$mask_octet )) -ne 0 ]; then
is_correct=0;
fi
fi
done
echo $is_correct;
}
#######################
## MAIN
#######################
OPTIND=1;
getopts "fibh" force;
shift $((OPTIND-1))
if [ $force = 'h' ]; then
echo ""
echo -e "THIS SCRIPT WILL EXPAND A CIDR ADDRESS.\n\nSYNOPSIS\n ./cidr-to-ip.sh [OPTION(only one)] [STRING/FILENAME]\nDESCRIPTION\n -h Displays this help screen\n -f Forces a check for network boundary when given a STRING(s)\n -i Will read from an Input file (no network boundary check)\n -b Will do the same as –i but with network boundary check\n\nEXAMPLES\n ./cidr-to-ip.sh 192.168.0.1/24\n ./cidr-to-ip.sh 192.168.0.1/24 10.10.0.0/28\n ./cidr-to-ip.sh -f 192.168.0.0/16\n ./cidr-to-ip.sh -i inputfile.txt\n ./cidr-to-ip.sh -b inputfile.txt\n"
exit
fi
if [ $force = 'i' ] || [ $force = 'b' ]; then
old_IPS=$IPS
IPS=$'\n'
lines=($(cat $1)) # array
IPS=$old_IPS
else
lines=$#
fi
for ip in ${lines[#]}; do
net=$(echo $ip | cut -d '/' -f 1);
prefix=$(echo $ip | cut -d '/' -f 2);
do_processing=1;
bit_netmask=$(prefix_to_bit_netmask $prefix);
wildcard_mask=$(bit_netmask_to_wildcard_netmask "$bit_netmask");
is_net_boundary=$(check_net_boundary $net "$wildcard_mask");
if [ $force = 'f' ] && [ $is_net_boundary -ne 1 ] || [ $force = 'b' ] && [ $is_net_boundary -ne 1 ] ; then
read -p "Not a network boundary! Continue anyway (y/N)? " -n 1 -r
echo ## move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]; then
do_processing=1;
else
do_processing=0;
fi
fi
if [ $do_processing -eq 1 ]; then
str=
for (( i = 1; i <= 4; i++ )); do
range=$(echo $net | cut -d '.' -f $i)
mask_octet=$(echo $wildcard_mask | cut -d ' ' -f $i)
if [ $mask_octet -gt 0 ]; then
range="{$range..$(( $range | $mask_octet ))}";
fi
str="${str} $range"
done
ips=$(echo $str | sed "s, ,\\.,g"); ## replace spaces with periods, a join...
eval echo $ips | tr ' ' '\n'
else
exit
fi
done
nmap is useful, but an overkill.
You can use prips instead. Saves you the hassle of grepping out the extra output from nmap and using awk.
Calling prips 192.168.0.0/23 will print what you need.
I use the following to skip the network address and broadcast: prips "$subnet" | sed -e '1d; $d'
Prips also has other useful options, e.g. being able to sample every n-th IP.
It's available via apt,brew,rpm and as tar.gz.
This short script will print all the IP addresses in a CIDR range in a few lines of Bash. (I named it prips after the Ubuntu command of the same name. Obviously, if that command is available, use that.)
prips() {
local cidr="$1" ; local lo hi a b c d e f g h
# range is bounded by network (-n) & broadcast (-b) addresses.
lo="$(ipcalc -n "$cidr" | cut -f2 -d=)"
hi="$(ipcalc -b "$cidr" | cut -f2 -d=)"
IFS=. read -r a b c d <<< "$lo"
IFS=. read -r e f g h <<< "$hi"
eval "echo {$a..$e}.{$b..$f}.{$c..$g}.{$d..$h}"
}
Note that I assume the RedHat Linux (Erik Troan, Preston Brown) version of ipcalc, not the Krischan Jodies version that is installed on some platforms (e.g. Mac OS X).
Examples:
$ prips 10.0.0.128/27
10.0.0.128 10.0.0.129 10.0.0.130 10.0.0.131 10.0.0.132 10.0.0.133 10.0.0.134 10.0.0.135 10.0.0.136 10.0.0.137 10.0.0.138 10.0.0.139 10.0.0.140 10.0.0.141 10.0.0.142 10.0.0.143 10.0.0.144 10.0.0.145 10.0.0.146 10.0.0.147 10.0.0.148 10.0.0.149 10.0.0.150 10.0.0.151 10.0.0.152 10.0.0.153 10.0.0.154 10.0.0.155 10.0.0.156 10.0.0.157 10.0.0.158 10.0.0.159
Calculates correct number of addresses in a /23 networks:
$ prips 10.0.0.0/23 | wc -w
512
Inspecting a few of those addresses using cut:
$ prips 10.0.0.0/23 | cut -f1-10,256-266 -d' '
10.0.0.0 10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4 10.0.0.5 10.0.0.6 10.0.0.7 10.0.0.8 10.0.0.9 10.0.0.255 10.0.1.0 10.0.1.1 10.0.1.2 10.0.1.3 10.0.1.4 10.0.1.5 10.0.1.6 10.0.1.7 10.0.1.8 10.0.1.9
And maybe too slow but also correctly generates the 16 million addresses in a /8 network:
$ date ; prips 10.0.0.0/8 | wc -w ; date
Sat May 20 18:06:00 AEST 2017
16777216
Sat May 20 18:06:41 AEST 2017
I recently wrote a function to generate all IP addresses from a given network address. The function takes the network address as argument and accepts CIDR and subnet masks. The script then stores all IPs in the array variable $ips.
Code
network_address_to_ips() {
# create array containing network address and subnet
local network=(${1//\// })
# split network address by dot
local iparr=(${network[0]//./ })
# if no mask given it's the same as /32
local mask=32
[[ $((${#network[#]})) -gt 1 ]] && mask=${network[1]}
# convert dot-notation subnet mask or convert CIDR to an array like (255 255 255 0)
local maskarr
if [[ ${mask} =~ '.' ]]; then # already mask format like 255.255.255.0
maskarr=(${mask//./ })
else # assume CIDR like /24, convert to mask
if [[ $((mask)) -lt 8 ]]; then
maskarr=($((256-2**(8-mask))) 0 0 0)
elif [[ $((mask)) -lt 16 ]]; then
maskarr=(255 $((256-2**(16-mask))) 0 0)
elif [[ $((mask)) -lt 24 ]]; then
maskarr=(255 255 $((256-2**(24-mask))) 0)
elif [[ $((mask)) -lt 32 ]]; then
maskarr=(255 255 255 $((256-2**(32-mask))))
elif [[ ${mask} == 32 ]]; then
maskarr=(255 255 255 255)
fi
fi
# correct wrong subnet masks (e.g. 240.192.255.0 to 255.255.255.0)
[[ ${maskarr[2]} == 255 ]] && maskarr[1]=255
[[ ${maskarr[1]} == 255 ]] && maskarr[0]=255
# generate list of ip addresses
local bytes=(0 0 0 0)
for i in $(seq 0 $((255-maskarr[0]))); do
bytes[0]="$(( i+(iparr[0] & maskarr[0]) ))"
for j in $(seq 0 $((255-maskarr[1]))); do
bytes[1]="$(( j+(iparr[1] & maskarr[1]) ))"
for k in $(seq 0 $((255-maskarr[2]))); do
bytes[2]="$(( k+(iparr[2] & maskarr[2]) ))"
for l in $(seq 1 $((255-maskarr[3]))); do
bytes[3]="$(( l+(iparr[3] & maskarr[3]) ))"
printf "%d.%d.%d.%d\n" "${bytes[#]}"
done
done
done
done
}
Example
network_address_to_ips 10.0.1.0/255.255.255.240
network_address_to_ips 10.1.0.0/24
This script should do. It's (almost) pure Bash. The seq part can be replaced if a completely pure bash is required.
Since Bash apparently uses signed two-complement 4-byte integers, the script is limited to /8 mask maximum. I found ranges larger than /16 impractical anyway so this doesn't bother me at all. If someone knows a simple way to overcome this, please share :)
#!/usr/bin/env bash
BASE_IP=${1%/*}
IP_CIDR=${1#*/}
if [ ${IP_CIDR} -lt 8 ]; then
echo "Max range is /8."
exit
fi
IP_MASK=$((0xFFFFFFFF << (32 - ${IP_CIDR})))
IFS=. read a b c d <<<${BASE_IP}
ip=$((($b << 16) + ($c << 8) + $d))
ipstart=$((${ip} & ${IP_MASK}))
ipend=$(((${ipstart} | ~${IP_MASK}) & 0x7FFFFFFF))
seq ${ipstart} ${ipend} | while read i; do
echo $a.$((($i & 0xFF0000) >> 16)).$((($i & 0xFF00) >> 8)).$(($i & 0x00FF))
done
Usage:
./script.sh 192.168.13.55/22
Tested with Bash version 4.4.23. YMMV.
I think this little script I hacked together does the trick. If not, it's definitely a starting point! Good luck.
#!/bin/bash
############################
## Methods
############################
prefix_to_bit_netmask() {
prefix=$1;
shift=$(( 32 - prefix ));
bitmask=""
for (( i=0; i < 32; i++ )); do
num=0
if [ $i -lt $prefix ]; then
num=1
fi
space=
if [ $(( i % 8 )) -eq 0 ]; then
space=" ";
fi
bitmask="${bitmask}${space}${num}"
done
echo $bitmask
}
bit_netmask_to_wildcard_netmask() {
bitmask=$1;
wildcard_mask=
for octet in $bitmask; do
wildcard_mask="${wildcard_mask} $(( 255 - 2#$octet ))"
done
echo $wildcard_mask;
}
#######################
## MAIN
#######################
for ip in $#; do
net=$(echo $ip | cut -d '/' -f 1);
prefix=$(echo $ip | cut -d '/' -f 2);
bit_netmask=$(prefix_to_bit_netmask $prefix);
wildcard_mask=$(bit_netmask_to_wildcard_netmask "$bit_netmask");
str=
for (( i = 1; i <= 4; i++ )); do
range=$(echo $net | cut -d '.' -f $i)
mask_octet=$(echo $wildcard_mask | cut -d ' ' -f $i)
if [ $mask_octet -gt 0 ]; then
range="{0..$mask_octet}";
fi
str="${str} $range"
done
ips=$(echo $str | sed "s, ,\\.,g"); ## replace spaces with periods, a join...
eval echo $ips | tr ' ' '\012'
done
I have extended #rberg script a little.
check if the "network" you provide really is a network (use -f to skip the check)
handle netmasks greater than /24
Maybe this is of use for someone.
#!/bin/bash
############################
## Methods
############################
prefix_to_bit_netmask() {
prefix=$1;
shift=$(( 32 - prefix ));
bitmask=""
for (( i=0; i < 32; i++ )); do
num=0
if [ $i -lt $prefix ]; then
num=1
fi
space=
if [ $(( i % 8 )) -eq 0 ]; then
space=" ";
fi
bitmask="${bitmask}${space}${num}"
done
echo $bitmask
}
bit_netmask_to_wildcard_netmask() {
bitmask=$1;
wildcard_mask=
for octet in $bitmask; do
wildcard_mask="${wildcard_mask} $(( 255 - 2#$octet ))"
done
echo $wildcard_mask;
}
check_net_boundary() {
net=$1;
wildcard_mask=$2;
is_correct=1;
for (( i = 1; i <= 4; i++ )); do
net_octet=$(echo $net | cut -d '.' -f $i)
mask_octet=$(echo $wildcard_mask | cut -d ' ' -f $i)
if [ $mask_octet -gt 0 ]; then
if [ $(( $net_octet&$mask_octet )) -ne 0 ]; then
is_correct=0;
fi
fi
done
echo $is_correct;
}
#######################
## MAIN
#######################
OPTIND=1;
getopts "f" force;
shift $(( OPTIND-1 ));
for ip in $#; do
net=$(echo $ip | cut -d '/' -f 1);
prefix=$(echo $ip | cut -d '/' -f 2);
do_processing=1;
bit_netmask=$(prefix_to_bit_netmask $prefix);
wildcard_mask=$(bit_netmask_to_wildcard_netmask "$bit_netmask");
is_net_boundary=$(check_net_boundary $net "$wildcard_mask");
if [ $force != 'f' ] && [ $is_net_boundary -ne 1 ]; then
read -p "Not a network boundary! Continue anyway (y/N)? " -n 1 -r
echo ## move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]; then
do_processing=1;
else
do_processing=0;
fi
fi
if [ $do_processing -eq 1 ]; then
str=
for (( i = 1; i <= 4; i++ )); do
range=$(echo $net | cut -d '.' -f $i)
mask_octet=$(echo $wildcard_mask | cut -d ' ' -f $i)
if [ $mask_octet -gt 0 ]; then
range="{$range..$(( $range | $mask_octet ))}";
fi
str="${str} $range"
done
ips=$(echo $str | sed "s, ,\\.,g"); ## replace spaces with periods, a join...
eval echo $ips | tr ' ' '\012'
fi
done
Wanted to comment on an answer above but don't have the rep yet.
Using the top solution with NMAP I added this to my .bashrc
expand-ip() {
nmap -sL -n -iL "$1" | awk '/Nmap scan report/{print $NF}'
}
Now I can use this with just expand-ip targs.
You can use this script (you need to have "bc" installed on your system):
for ip in $# ;do
net=$(echo $ip | cut -d '/' -f 1);
prefix=$(echo $ip | cut -d '/' -f 2);
o1=$(echo $net | cut -d '.' -f4);
o2=$(echo $net | cut -d '.' -f3);
o3=$(echo $net | cut -d '.' -f2);
o4=$(echo $net | cut -d '.' -f1);
len=$(echo "2^(32 - $prefix)"|bc);
for i in `seq $len`;do
echo "$o4.$o3.$o2.$o1";
o1=$(echo "$o1+1"|bc);
if [ $o1 -eq 256 ]; then
o1=0;
o2=$(echo "$o2+1"|bc);
if [ $o2 -eq 256 ]; then
o2=0;
o3=$(echo "$o3+1"|bc);
if [ $o3 -eq 256 ]; then
o3=0;
o4=$(echo "$o4+1"|bc);
fi
fi
fi
done
done
fping -Aaqgr 1 10.1.1.0/24
Simplicity works best

Resources