生成N个有效IP地址的脚本? [英] Script to generate N number of valid ip addresses?

查看:27
本文介绍了生成N个有效IP地址的脚本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 TCL 的新手,并试图通过编写一些简单的脚本来学习,我已经开始编写一个简单的脚本,该脚本从给定的起始 ip 地址生成有效的 ip 地址.

I am new to TCL and trying to learn by doing some simple scripting, I have taken upon to write a simple script which generates valid ip address from a given starting ip address.

我已经设法写了一个,但遇到了两个问题,

I have managed to write one but have run into two problems,

  1. 最后一个八位字节在数字 192.168.1.025 前面添加了一个零
  2. 当我指定像这样的起始 ip 250.250.5.1 时,它无法生成正确的 ip,

下面是我的代码:

proc generate {start_addr total_addr} {
    if {$total_addr == 0} {return}
    regexp {([0-9]+\.)([0-9]+\.)([0-9]+\.)([0-9]+)} $start_addr match a b c d
    set filename "output.txt"
    set fileId [open $filename "a"]
    puts $fileId $a$b$c$d
    close $fileId
    while {$a<255 && $b <255 && $c <255 && $d < 255 } {
        set d [expr {$d + 1}];
        set filename "output.txt"
        set fileId [open $filename "a"]
        puts $fileId $a$b$c$d
        close $fileId
        set total_addr [expr {$total_addr - 1}];
        if {$total_addr == 1} {return}
        if {$total_addr > 1 && $d == 255} {
            set c [expr {$c + 1}];
            set d 1
            set filename "output.txt"
            set fileId [open $filename "a"]
            puts $fileId $a$b$c$d
            close $fileId
            set total_addr [expr {$total_addr - 1}];
        }
        if {$total_addr > 1 && $c==255 && $d == 255} {
            set b [expr {$b + 1}];
            set c 1
            set d 1
            set filename "output.txt"
            set fileId [open $filename "a"]
            puts $fileId $a$b$c$d
            close $fileId
            set total_addr [expr {$total_addr - 1}];
        }
        if {$total_addr > 1 && $b == 255 && $c == 255 && $d == 255} {
            set a [expr {$a + 1}];
            set b 1
            set c 1
            set d 1
            set filename "output.txt"
            set fileId [open $filename "a"]
            puts $fileId $a$b$c$d
            close $fileId
            set total_addr [expr {$total_addr - 1}];
        }
    }
}


flush stdout
puts "Please enter the starting IPv4 address with . as delimiter EX: 1.1.1.1"
set start_addr [gets stdin]
regexp {([0-9]+\.)([0-9]+\.)([0-9]+\.)([0-9]+)} $start_addr match a b c d
if {$a <= 255 & $b <= 255 & $c <= 255 & $d <= 255} {
    puts "this is a valid ip address"
} else {
    puts "this not a valid ip address"
}
flush stdout
puts "Please enter the total number of IPv4 address EX: 1000"
set total_addr [gets stdin]
set result [generate $start_addr $total_addr]

推荐答案

拆分地址

除了使用 Tcllib 中的 ip 包外,还有几种方法可以拆分 IPv4点十进制"地址并将八位字节值放入四个变量中.你使用的是

Splitting the address

Apart from using the ip package in Tcllib, there are a few ways to split up an IPv4 "dot-decimal" address and put the octet values into four variables. The one you used was

regexp {([0-9]+\.)([0-9]+\.)([0-9]+\.)([0-9]+)} $start_addr match a b c d

这基本上是有效的,但它有几个问题.第一个问题是地址1.234.1.234会被拆分为1. 234. 1. 234,然后当您尝试对前三个变量使用 incr 命令时,您将收到一条错误消息(我想这就是您使用 expr {$x + 1}而不是 incr).相反,写

This basically works, but there are a couple of problems with it. The first problem is that the address 1.234.1.234 will be split up as 1. 234. 1. 234, and then when you try to use the incr command on the first three variables you will get an error message (I suppose that's why you used expr {$x + 1} instead of incr). Instead, write

regexp {(\d+)\.(\d+)\.(\d+)\.(\d+)} $start_addr match a b c d

此表达式将点放在捕获括号之外,并将整数值放入变量中.使用速记 \d(十进制数字)代替 [0-9] 集也是一个好主意.但你也可以这样做:

This expression puts the dots outside the capturing parentheses and places integer values into the variables. It's also a good idea to use the shorthand \d (decimal digit) instead of the [0-9] sets. But you could also do this:

regexp -all -inline -- {\d+} $start_addr

您只需让 regexp 收集所有 (-all) 十进制数字的完整序列并将它们作为列表返回 (-inline).由于您以列表形式获得结果,因此您需要将它们lassign(列表分配)放入变量中:

where you simply ask regexp to collect all (-all) unbroken sequences of decimal digits and return them as a list (-inline). Since you get the result as a list, you then need to lassign (list assign) them into variables:

lassign [regexp -all -inline -- {\d+} $start_addr] a b c d

但是如果你可以不用正则表达式,你应该这样做.唐纳建议

But if you can make do without a regular expression, you should. Donal suggested

scan $start_addr "%d.%d.%d.%d" a b c d

这很好.另一种方法是在点处split 字符串:

which is fine. Another way is to split the string at the dots:

lassign [split $start_addr .] a b c d

(同样,您会得到一个列表作为结果,并且需要在第二步中将其分配给您的变量).

(again you get a list as the result and need to assign it to your variables in a second step).

正如 Donal 所写,每当您根据用户输入(以及在许多其他情况下)创建数据时,最好检查一下您是否得到了预期的结果.如果您使用赋值 regexp,则该命令根据匹配成功还是失败返回 1 或 0.这个结果可以直接插入到 if 调用中:

As Donal wrote, it's a good idea whenever you create data from user input (and in many other situations as well) to check that you did get what you expected to get. If you use an assigning regexp the command returns 1 or 0 depending on whether the matched succeeded or failed. This result can be plugged directly into an if invocation:

if {![regexp {(\d+)\.(\d+)\.(\d+)\.(\d+)} $start_addr match a b c d]} {
    error "input data didn't match IPv4 dot-decimal notation"
}

Donal 已经给出了一个检查scan结果的例子.在这种情况下,您检查 4,因为该命令返回它管理的成功匹配数.

Donal already gave an example of checking the result of scan. In this case you check against 4 since the command returns the number of successful matches it managed.

if {[scan $start_addr "%d.%d.%d.%d" a b c d] != 4} {
    error "input data didn't match IPv4 dot-decimal notation"
}

如果您使用任一列表创建命令(内联 regexpsplit),您可以检查结果的列表长度:

If you use either of the list-creating commands (inline regexp or split) you can check the list length of the result:

if {[llength [set result [split $start_addr .]]] == 4} {
    lassign $result a b c d
} else {
    error "input data didn't match IPv4 dot-decimal notation"
}

此检查之后应检查所有变量的八位字节值 (0-255).一种方便的方法是这样的:

This check should be followed by checking all variables for octet values (0-255). One convenient way to do this is like this:

proc isoctet args {
    ::tcl::mathop::* {*}[lmap octet $args {expr {0 <= $octet && $octet <= 255}}]
}

(将测试分解为函数通常是个好主意;如果您在代码的多个位置使用测试,这实际上是法律*.)

(It's usually a good idea to break out tests as functions; it's practically the law* if you are using the tests in several places in your code.)

这个命令,isoctet,将许多值作为参数,将它们作为一个列表集中在特殊参数args 中.lmap 命令创建一个与原始列表具有相同元素数量的新列表,其中每个元素的值是将给定脚本应用于原始列表中相应元素的结果.在这种情况下,lmap 会根据值是否为真正的八位字节值生成一个 1 和 0 列表.示例:

This command, isoctet, takes a number of values as arguments, lumping them together as a list in the special parameter args. The lmap command creates a new list with the same number of elements as the original list, where the value of each element is the result of applying the given script to the corresponding element in the original list. In this case, lmap produces a list of ones and zeros depending on whether the value was a true octet value or not. Example:

input list:  1 234 567 89
result list: 1   1   0  1

结果列表然后由 {*} 扩展为 ::tcl::mathop::* 命令的单个参数,然后将它们相乘.为什么?因为如果 1 和 0 可以作为真值和假值,那么一串零和一串的乘积恰好与同一个表的逻辑与(AND,&&)完全一样.

The resulting list is then expanded by {*} into individual arguments to the ::tcl::mathop::* command, which multiplies them together. Why? Because if 1 and 0 can be taken as true and false values, the product of a list of ones and zeros happens to be exactly the same as the logical conjunction (AND, &&) of the same list.

result 1: 1 1 0 1
product : 0 (false)
result 2: 1 1 1 1
product : 1 (true)

所以,

if {![isoctet $a $b $c $d]} {
    error "one of the values was outside the (0, 255) range"
}

生成新地址

生成新地址的最不性感的方式可能是使用 Tcl 中的现成工具:binary.

binary scan [binary format c* [list $a $b $c $d]] I n

此调用首先将整数值列表(同时将它们限制为八位字节大小)转换为位字符串,然后将该位字符串解释为大端 32 位整数(如果您的机器使用小端整数,您应该使用转换说明符 i 而不是 I).

This invocation first converts a list of integer values (while constraining them to octet size) to a bit string, and then interprets that bit string as a big-endian 32-bit integer (if your machine uses little-endian integers, you should use the conversion specifier i instead of I).

增加数字.哇!

incr n

将其转换回 8 位值列表:

Convert it back to a list of 8-bit values:

binary scan [binary format I $n] c4 parts

parts 的组成部分现在是有符号 8 位整数,即最高值为 127,应该大于 127 的值现在是负值.将值转换为无符号 (0 - 255) 值,如下所示:

The components of parts are now signed 8-bit integers, i.e. the highest value is 127, and the values that should be higher than 127 are now negative values. Convert the values to unsigned (0 - 255) values like this:

lassign [lmap part $parts {expr {$part & 0xff}}] a b c d

并将它们连接成一个点十进制字符串,如下所示:

and join them up to a dot-decimal string like this:

set addr [join [list $a $b $c $d] .]

如果您想要多个新地址,请重复此过程.

If you want more than one new address, repeat the process.

文档:二进制错误, expr, ifa>、incr加入lassign, llength, lmap, mathop, proc正则表达式扫描设置拆分{*}

Documentation: binary, error, expr, if, incr, join, lassign, llength, lmap, mathop, proc, regexp, scan, set, split, {*}

lmap 是 Tcl 8.6 命令.Tcl 8.4 和 8.5 的纯 Tcl 实现可在此处获得.

lmap is a Tcl 8.6 command. Pure-Tcl implementations for Tcl 8.4 and 8.5 are available here.

*) 如果有任何法律.您必须了解的是,这些规则与矩阵的规则没有什么不同.其中一些可以弯曲.其他人可能会被打破.

*) If there were any laws. What you must learn is that these rules are no different than the rules of the Matrix. Some of them can be bent. Others can be broken.

这篇关于生成N个有效IP地址的脚本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆