【问题标题】:Using a variable between regexp inside foreach在 foreach 内的正则表达式之间使用变量
【发布时间】:2015-01-31 23:02:51
【问题描述】:

我需要根据与 checkpoint 和 cisco 供应商匹配的列表获取所有 mac。我在 Cisco 路由器上执行“显示 ip arp”,捕获 arp 输出并保存在文件中。现在,我需要逐行检查我拥有的 Mac。

确切地说,我只需要匹配包含 Cisco 和 Checkpoint 供应商的 mac 地址的行。为此,我必须获取 IP 地址、前四个十六进制数字、点和更多两个十六进制数字来定义 mac 供应商,例如(001c.7f 或 e02f.6d)并在文件上打印这一行。之后,我需要比较“IP-ARP.txt”和“MAC-ADDRESS.txt”(最后一个包含完整的 MAC 地址供应商)。如果我的输出匹配,将此行保存在另一个文件中。

这里有一段文件:

IP-ARP.txt

Internet  172.20.14.12            0   001c.7f41.186e  ARPA

Internet  172.20.14.13           57   001c.7f41.074e  ARPA

Internet  172.20.14.14            0   0200.5ebd.e17d  ARPA

Internet  172.20.19.11            -   7081.050f.9402  ARPA

Internet  172.20.19.12           54   7cad.7499.e602  ARPA

Internet  172.20.19.13            7   e02f.6d14.c1bf  ARPA

Internet  172.20.19.14          104   e02f.6d15.1d7f  ARPA

MAC-ADDRESS.txt

001c.7f

001c.ab

001b.de

001b.ff

001c.cd

001c.de

e02f.6c

e02f.7c

提前致谢!

【问题讨论】:

  • @Bohemian 完成!我改写并删除我的 cmets。谢谢!

标签: regex foreach tcl cisco checkpoint


【解决方案1】:

由于规格变化再次更新

我们这里有一个识别字符串列表(OUI MAC 地址部分写成 MAC 地址字符串的片段)和一个需要对照此列表检查的数据字符串列表。

我的解决方案使用 Tcl 库中的 fileutil 包。因为您可以使用标准的 Tcl 命令,所以这不是很必要,但它大大简化了脚本。

package require fileutil

定义一些要使用的文件名。

set filename(macaddr) MAC-ADDRESS.txt
set filename(iparp)   IP-ARP.txt
set filename(output)  output.txt

如果识别字符串列表可能会发生变化,您可能需要在每次运行脚本时从文件中读取它:

set idlist [::fileutil::cat $filename(macaddr)]

或者,如果这些地址很少更改,您可以在脚本中硬编码并在必要时进行编辑:

set idlist {001c.7f 001c.ab 001b.de 001b.ff 001c.cd 001c.de e02f.6c e02f.7c}

将输出文件的内容设置为空字符串。

::fileutil::writeFile $filename(output) {}

要选择IP-ARP.txt 文件中与这些地址中的任何一个匹配的行,有几种方法可以遍历它。我的建议是使用fileutil::foreachLine 命令。基本调用是这样的:

::fileutil::foreachLine varName filename script

(第一个参数是一个任意变量名:在每次迭代时,当前行将存储在该变量中。第二个是要遍历的文件的名称,第三个参数是每行运行一次的脚本在那个文件中。)

脚本使用string match 命令调用与id 字符串匹配的命令。可以改用regexp 命令,但我认为在这种情况下这是完全没有必要的。 IP-ARP.txt 文件中的每一行要么是空白,要么是包含五个元素的正确 Tcl 列表,其中 MAC 地址是第四个。此外,第二个元素是 ip 号,仅使用以 172 开头的那些。这意味着匹配的命令可以这样写:

proc matchid {idlist line} {
    set ipAddr  [lindex $line 1]
    set macAddr [lindex $line 3]

    if {[string match 172* $ipAddr]} {
        foreach id $idlist {
            if {[string match $id* $macAddr]} {
                return "$ipAddr $macAddr\n"
            }
        }
    }
}

(只有当地址是点分十进制形式时,以这种方式匹配ip地址才有效:如果它可以是任何其他形式,则应使用Tcllib ip模块来匹配它。)

如果行匹配,则命令的结果是包含 ip 地址和 MAC 地址的行,如果不匹配,则为空字符串。

现在让我们遍历IP-ARP.txt 文件的内容。对于每一行,将内容与 id 列表进行匹配,并获取输出字符串或空字符串。如果字符串不为空,请将其附加到输出文件中。

::fileutil::foreachLine line $filename(iparp) {
    set res [matchid $idlist $line]

    if {$res ne {}} {
        ::fileutil::appendToFile $filename(output) $res 
    }
}

就是这样。完整的程序:


package require fileutil

set filename(macaddr) MAC-ADDRESS.txt
set filename(iparp)   IP-ARP.txt
set filename(output)  output.txt

set idlist [::fileutil::cat $filename(macaddr)]

::fileutil::writeFile $filename(output) {}

proc matchid {idlist line} {
    set ipAddr  [lindex $line 1]
    set macAddr [lindex $line 3]

    if {[string match 172* $ipAddr]} {
        foreach id $idlist {
            if {[string match $id* $macAddr]} {
                return "$ipAddr $macAddr\n"
            }
        }
    }
}

::fileutil::foreachLine line $filename(iparp) {
    set res [matchid $idlist $line]

    if {$res ne {}} {
        ::fileutil::appendToFile $filename(output) $res 
    }
}

Tcllib fileutil module 的文档

文档:foreachiflindexpackageprocsetstring

(注意:cmets中提到的'Hoodiecrow'是我,我之前用过那个昵称。)

【讨论】:

  • 你想要read -nonewline,否则foreach循环的最后一次迭代将执行一个空的$a
  • @Hoodiecrow 再次感谢!我明天将在 GMT -3 试一试。之后,我发布结果!
  • @Hoodiecrow 完成!我改写并删除我的 cmets。谢谢!
【解决方案2】:

给定变量$mac中的部分mac地址(形式为“xxxx.xx”),以匹配包含以该值开头的mac地址的行:

^.*\b$mac[0-9a-f]{2}\.[0-9a-f]{4}\b.*$

如果您的语言匹配包含模式的行,则可以省略换行:

\b$mac[0-9a-f]{2}\.[0-9a-f]{4}\b

【讨论】:

  • ITYM \y: \b 匹配 Tcl re 语法中的退格。如果要匹配带有大写 A-F 的十六进制数字,这些模式也会失败。即使有\y 字限制,这些模式也会匹配第二组 MAC 地址。
【解决方案3】:

根据 Hoodiecrow 的回答,我做到了:

set Ouilist { 0000.0c 0001.42 0001.43 0001.63 0001.64 ... }

set Macs1 [open "IP-ARP.txt" r]

foreach a [split [read -nonewline $Macs1] \n] {

set macAddr [lindex $a 3]

set IP [lindex $a 1]

   if { [regexp {(172)\.([0-9]+)\.([0-9]+)\.([0-9]+)} $IP RealIP] } {

       regexp {([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])\.([0-9a-f])([0-9a-f])} $macAddr OuiPart
       if { $OuiPart in $Ouilist } {
          puts "$RealIP $macAddr\r
       }

   }

这样我就可以得到所有以 172 开头的 ips 和 mac-address 都是 Cisco 和 Checkpoint 供应商。

【讨论】:

  • 抱歉,但是哎哟。你还在使用regexp 命令,你真的不应该。我会更新我的答案以反映您的规范更改。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-16
  • 2020-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多