22

我正在尝试创建可以输入一组前缀的脚本,然后将列出前缀中的所有 IP 地址(包括网络/主机/广播)。

一个例子是:

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

有一些 python/perl 脚本可以做到这一点,但我希望有一个简单的 bash 脚本,因为它可以在没有 perl/python 的系统上使用(是的..我知道..)

4

11 回答 11

50

这是我用来在给定 CIDR 块中生成所有 IP 地址的方法

nmap -sL -n 10.10.64.0/27 | awk '/Nmap scan report/{print $NF}'

nmap手册页中,标志是:

-sL: List Scan - simply list targets to scan
-n: Never do DNS resolution

就这么简单

上面的命令输出这个

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
于 2015-07-14T16:49:59.930 回答
21

我也在寻找这个解决方案,发现@scherand 脚本效果很好。我还添加到此脚本中,为您提供更多选择。帮助文件如下。

此脚本将扩展一个CIDR地址。

概要

./cidr-to-ip.sh [OPTION(only one)] [STRING/FILENAME]

描述

-h 显示此帮助屏幕

-f 在给定 STRING(s) 时强制检查网络边界

-i 将从输入文件中读取(文件每行应包含一个 CIDR)(无网络边界检查)

-b 将与 –i 执行相同的操作,但会进行网络边界检查

例子

./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
于 2015-01-21T02:33:54.793 回答
11

这个简短的脚本将在几行 Bash 中打印 CIDR 范围内的所有 IP 地址。(我以同名pripsUbuntu 命令命名它。显然,如果该命令可用,请使用它。)

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}"
}

请注意,我假设的是 RedHat Linux(Erik Troan,Preston Brown)版本ipcalc,而不是安装在某些平台(例如 Mac OS X)上的 Krischan Jodies 版本。

例子:

$ 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

计算 /23 网络中正确的地址数:

$ prips 10.0.0.0/23 | wc -w 
512

使用 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

也许太慢了,但也能在 /8 网络中正确生成 1600 万个地址:

$ 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
于 2017-05-16T12:29:43.977 回答
9

nmap很有用,但有点矫枉过正。

你可以prips改用。为您省去从nmap和使用awk.

呼叫prips 192.168.0.0/23将打印您需要的内容。

我使用以下内容跳过网络地址和广播:prips "$subnet" | sed -e '1d; $d'

Prips 还有其他有用的选项,例如能够对每个第 n 个 IP 进行采样。

它可以通过aptbrewrpmas 获得tar.gz

于 2018-09-25T14:51:32.130 回答
7

我最近编写了一个函数来从给定的网络地址生成所有 IP 地址。该函数将网络地址作为参数并接受 CIDR 和子网掩码。然后,该脚本将所有 IP 存储在数组变量 $ips 中。

代码

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
}

例子

network_address_to_ips 10.0.1.0/255.255.255.240
network_address_to_ips 10.1.0.0/24
于 2013-08-08T07:41:21.433 回答
5

这个脚本应该可以。它(几乎)是纯 Bash。seq如果需要完全纯 bash,则可以更换该部件。

由于 Bash 显然使用带符号的双补码 4 字节整数,因此脚本被限制为 /8 掩码最大值。无论如何,我发现大于 /16 的范围是不切实际的,所以这根本不会打扰我。如果有人知道克服这个问题的简单方法,请分享:)

#!/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

用法:

./script.sh 192.168.13.55/22

使用 Bash 版本 4.4.23 进行测试。YMMV。

于 2019-10-03T11:32:18.250 回答
2

我认为我一起编写的这个小脚本可以解决问题。如果没有,那绝对是一个起点!祝你好运。

#!/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
于 2013-06-11T12:40:50.150 回答
1

我稍微扩展了@rberg 脚本。

  • 检查您提供的“网络”是否真的是网络(用于-f跳过检查)
  • 处理大于/24

也许这对某人有用。

#!/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
于 2014-03-19T07:59:26.493 回答
1

想对上面的答案发表评论,但还没有代表。

使用带有 NMAP 的顶级解决方案,我将其添加到我的.bashrc

expand-ip() {
  nmap -sL -n -iL "$1" | awk '/Nmap scan report/{print $NF}'
}

现在我可以将它与 just 一起使用expand-ip targs

于 2017-08-27T19:44:24.660 回答
0

您可以使用此脚本
(您需要在系统上安装“bc”):

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
于 2014-06-08T11:07:00.070 回答
0
fping -Aaqgr 1 10.1.1.0/24

简单最有效

于 2018-11-18T15:24:08.817 回答