Bash 脚本以列出前缀中的所有 IP

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16986879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:48:59  来源:igfitidea点击:

Bash script to list all IPs in prefix

baship-address

提问by user2463938

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

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

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

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

回答by John Vossler

Here is what I use to generate all the IP addresses in a given CIDR block

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

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

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

回答by Kyoungs

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.

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

THIS SCRIPT WILL EXPAND A CIDRADDRESS.

此脚本将扩展CIDR地址。

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

-h 显示此帮助屏幕

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

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

-b 与 -i 相同,但具有网络边界检查

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

./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=;
    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=;
    wildcard_mask=
    for octet in $bitmask; do
        wildcard_mask="${wildcard_mask} $(( 255 - 2#$octet ))"
    done
    echo $wildcard_mask;
}

check_net_boundary() {
    net=;
    wildcard_mask=;
    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 )) # 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

回答by Alex Harvey

This short script will print all the IP addresses in a CIDR range in a few lines of Bash. (I named it pripsafter the Ubuntu command of the same name. Obviously, if that command is available, use that.)

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

prips() {
  local cidr= ; 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).

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

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:

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

$ prips 10.0.0.0/23 | wc -w 
512

Inspecting a few of those addresses using cut:

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

也许太慢了,但也能在 /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

回答by Jakub Bochenski

nmapis useful, but an overkill.

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

You can use pripsinstead. Saves you the hassle of grepping out the extra output from nmapand using awk.

你可以prips改用。节省你grepping出额外输出的麻烦nmap和使用awk

Calling prips 192.168.0.0/23will print what you need.

调用prips 192.168.0.0/23将打印您需要的内容。

I use the following to skip the network address and broadcast: prips "$subnet" | sed -e '1d; $d'

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

Prips also has other useful options, e.g. being able to sample every n-th IP.

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

It's available via apt,brew,rpmand as tar.gz.

这是通过使用aptbrewrpmtar.gz

回答by Florian Feldhaus

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.

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

Code

代码

function network_address_to_ips() {
  # define empty array to hold the ip addresses
  ips=()
  # create array containing network address and subnet
  network=(${1//\// })
  # split network address by dot
  iparr=(${network[0]//./ })
  # check for subnet mask or create subnet mask from CIDR notation
  if [[ ${network[1]} =~ '.' ]]; then
    netmaskarr=(${network[1]//./ })
  else
    if [[ $((8-${network[1]})) -gt 0 ]]; then
      netmaskarr=($((256-2**(8-${network[1]}))) 0 0 0)
    elif  [[ $((16-${network[1]})) -gt 0 ]]; then
      netmaskarr=(255 $((256-2**(16-${network[1]}))) 0 0)
    elif  [[ $((24-${network[1]})) -gt 0 ]]; then
      netmaskarr=(255 255 $((256-2**(24-${network[1]}))) 0)
    elif [[ $((32-${network[1]})) -gt 0 ]]; then 
      netmaskarr=(255 255 255 $((256-2**(32-${network[1]}))))
    fi
  fi
  # correct wrong subnet masks (e.g. 240.192.255.0 to 255.255.255.0)
  [[ ${netmaskarr[2]} == 255 ]] && netmaskarr[1]=255
  [[ ${netmaskarr[1]} == 255 ]] && netmaskarr[0]=255
  # generate list of ip addresses
  for i in $(seq 0 $((255-${netmaskarr[0]}))); do
    for j in $(seq 0 $((255-${netmaskarr[1]}))); do
      for k in $(seq 0 $((255-${netmaskarr[2]}))); do
        for l in $(seq 1 $((255-${netmaskarr[3]}))); do
          ips+=( $(( $i+$(( ${iparr[0]}  & ${netmaskarr[0]})) ))"."$(( $j+$(( ${iparr[1]} & ${netmaskarr[1]})) ))"."$(($k+$(( ${iparr[2]} & ${netmaskarr[2]})) ))"."$(($l+$((${iparr[3]} & ${netmaskarr[3]})) )) )
        done
      done
    done
  done
}

Example

例子

network_address_to_ips 10.0.1.0/255.255.255.240
echo ${ips[@]}
network_address_to_ips 10.1.0.0/24
echo ${ips[@]}

回答by rberg

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=;
    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=;
    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 ' ' '2'

done

回答by scherand

I have extended @rberg script a little.

我稍微扩展了@rberg 脚本。

  • check if the "network" you provide really is a network (use -fto skip the check)
  • handle netmasks greater than /24
  • 检查您提供的“网络”是否真的是网络(用于-f跳过检查)
  • 处理大于 /24

Maybe this is of use for someone.

也许这对某人有用。

#!/bin/bash

############################
##  Methods
############################   
prefix_to_bit_netmask() {
    prefix=;
    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=;
    wildcard_mask=
    for octet in $bitmask; do
        wildcard_mask="${wildcard_mask} $(( 255 - 2#$octet ))"
    done
    echo $wildcard_mask;
}

check_net_boundary() {
    net=;
    wildcard_mask=;
    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 ' ' '2'
    fi

done

回答by Trevor Steen

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

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

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

Now I can use this with just expand-ip targs.

现在我可以将它与expand-ip targs.

回答by George

fping -Aaqgr 1 10.1.1.0/24

Simplicity works best

简单效果最好

回答by mikky

This script should do. It's (almost) pure Bash. The seqpart can be replaced if a completely pure bash is required.

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

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

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

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

使用 Bash 版本 4.4.23 进行测试。天啊。