Bash script to list all IPs in prefix

2020-05-15 05:08发布

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.. )

11条回答
▲ chillily
2楼-- · 2020-05-15 05:24

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.

查看更多
手持菜刀,她持情操
3楼-- · 2020-05-15 05:26

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
查看更多
劳资没心,怎么记你
4楼-- · 2020-05-15 05:29
fping -Aaqgr 1 10.1.1.0/24

Simplicity works best

查看更多
够拽才男人
5楼-- · 2020-05-15 05:30

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
查看更多
等我变得足够好
6楼-- · 2020-05-15 05:30

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
查看更多
闹够了就滚
7楼-- · 2020-05-15 05:34

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=${1%/*}
masksize=${1#*/}

[ $masksize -lt 8 ] && { echo "Max range is /8."; exit 1;}

mask=$(( 0xFFFFFFFF << (32 - $masksize) ))

IFS=. read a b c d <<< $base

ip=$(( ($b << 16) + ($c << 8) + $d ))

ipstart=$(( $ip & $mask ))
ipend=$(( ($ipstart | ~$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.

查看更多
登录 后发表回答