1

我正在生成一个 TCL 过程,它将返回一个开关下的设备数组列表。定义是一个读取的 XML 文件。使用递归过程解析生成的 XML 条目列表,并将设备属性放置在数组中。

然后将每个数组放在一个列表中并反射回调用者。我的问题是,当我打印出设备列表时,每次都会打印出添加到列表中的最后一个设备。列表的内容都是重复的。

注意:我使用的是在这里找到的优秀的 proc,'xml2list'。对不起,我忘了是谁提交的。

下面的代码说明了这个问题:

source C:/src/tcl/xml2list.tcl

# Read and parse XML file
set fh [open C:/data/tcl/testfile.xml r]
set myxml [read $fh]
set mylist [xml2list $myxml]


array set mydevice {}

proc devicesByName { name thelist list_to_fill} {
        global mydevice
        global set found_sw 0

        upvar $list_to_fill device_arr
        foreach switch [lindex $thelist 2] {

            set atts [lindex $switch 1]
            if { [lindex $switch 0] == "Switch" } {
                if { $name == [lindex $atts 3] } {
                    set found_sw 1
                    puts "==== Found Switch: $name ===="
                } else {
                    set found_sw 0
                }
            } elseif { $found_sw == 1 && [string length [lindex $atts 3]] > 0 } {

                set mydevice(hdr) [lindex $switch 0]
                set mydevice(port) [lindex $atts 1]
                set mydevice(name) [lindex $atts 3]
                set mydevice(type) [lindex $atts 5]
                puts "Device Found: $mydevice(name)"
                set text [lindex $switch 2]
                set mydevice(ip) [lindex [lindex $text 0] 1]

                lappend device_arr mydevice
            }
            devicesByName $name $switch device_arr
         }
    }

    #--- Call proc here

    # set a local array var and send to the proc
    set device_arr {}
    devicesByName "Switch1" $mylist device_arr

    # read out the contents of the list of arrays
    for {set i 0} {$i<[llength $device_arr]} {incr i} {
        upvar #0 [lindex $device_arr $i] temp
        if {[array exists temp]} {
            puts "\[$i\] Device: $temp(name)-$temp(ip)"
        }
    }

XML 文件在这里:

<Topology>    
    <Switch ports="48" name="Switch1" ip="10.1.1.3">
        <Device port="1" name="RHEL53-Complete1" type="host">10.1.1.10</Device>
        <Device port="2" name="Windows-Complete1" type="host">10.1.2.11</Device>
   <Device port="3" name="Solaris-Complete1" type="host">10.1.2.12</Device>
    </Switch>
    <Switch ports="36" name="Switch2" ip="10.1.1.4">
        <Device port="1" name="Windows-Complete2" type="host">10.1.3.10</Device>
    </Switch>
    <Router ports="24" name="Router1" ip="10.1.1.2">
        <Device port="1" name="Switch1" type="switch">10.1.1.3</Device>
        <Device port="2" name="Switch2" type="switch">10.1.1.4</Device>
    </Router>
</Topology>

如果我的代码块看起来很糟糕,请原谅。当我阅读它们时,我按照指示进行操作,但它看起来不正确。我无法修复它,所以还是发布了。

提前致谢...

4

3 回答 3

2

tcl 中的数组不是值。因此它们的行为不像常规变量。它们实际上是一些特殊的东西,比如文件句柄或套接字。

您不能将数组分配给这样的列表。正在做:

lappend device_arr mydevice

只需将字符串附加"mydevice"到列表中device_arr。该字符串恰好是全局变量的名称,因此以后可以使用该字符串来访问该全局变量。

要建立一个键值数据结构,你想要的是一个dict。您可以将 dict 视为具有偶数个元素的特殊列表,格式为:{key value key value}. 事实上,这种数据结构甚至在引入dicts 之前的非常旧版本的 tcl 上也有效,因为foreachtcl 中的循环可用于处理键值对。

所以你想要的是在$mydevice每个循环中创建一个新的 dict 并[dict set]用来分配值。

或者,您可以保留大部分代码并将 lappend 更改为:

lappend device_arr [array get mydevice]

这是有效的,因为[array get]返回一个可以被视为字典的键值列表。您可以稍后使用该dict命令访问数据。

于 2012-11-16T19:43:01.020 回答
2

数组变量不能用作值。要将其中的内容放入列表元素,将其发送到 proc,将其写入文件等,将其转换为列表形式(键、值、键、值...)array get

lappend device_arr [array get mydevice]

要稍后使用它,请将列表写回到带有array set.

foreach device_l $device_arr {
  #array unset device
  array set device $device_l
  puts "$device(name)-$device(ip)"
}

请注意,array set不会删除目标数组中的旧键,因此如果您在循环中使用它并且键名并不总是相同,则需要在每次迭代时清除数组。

于 2012-11-16T20:47:41.797 回答
0

您可以使用数组以两种方式存储此信息。第一个是多维数组,在这种情况下是一个三维数组,第二个是一个一维数组,存储一个列表,稍后可以轻松地将其转换为数组,以便以后访问数据。

对于 3d 数组,键是 Switch Name,device_port,dataname 您可以将错误的临时 myDevice 和 lappend 代码更改为

  # attr is a list of { attributename1 value1  ... attributenameN valueN}
  array set temp $attr
  set port $temp(port)
  set text [lindex $switch 2]
  set ip [lindex [lindex $text 0] 1]
  # name already set to "Switch1" etc
  foreach f [array names temp ] {
      set device_arr($name,$port,$f) $temp($f)
  } 
  set device_arr($name,$port,ip) $ip
  array unset temp

此代码导致以下结果(当 parray device_arr

parray device_arr

device_arr(Switch1,1,name) "Switch1"
device_arr(Switch1,1,port)  1
device_arr(Switch1,1,type) "RedHat .."  
device_arr(Switch1,1,ip) 10..
device_arr(Switch1,2,name) "Switch1"
device_arr(Switch1,2,port)  1
device_arr(Switch1,2,type) "RedHat .."  
device_arr(Switch1,2,ip) 10..
...
device_arr(Switch2,1,name) "Switch1"
device_arr(Switch2,1,port)  1
device_arr(Switch2,1,type) "Windows Complete"  
device_arr(Switch2,1,ip) 10..
....

要查找 Switch1 端口 2 的 ip,您将:

puts "the ip of Switch1 port 2 is $device_arr(Switch1,2,ip)"

请注意大量数据重复,但您可以直接访问所有数据,而无需像下一个方案中那样进入中间步骤来获取数据

  # attr is a list of { attributename1 value1  ... attributenameN valueN}
  set data $attr
  array set temp $attr
  set text [lindex $switch 2]
  set ip [lindex [lindex $text 0] 1]
  lappend data ip $ip 
   set key "$name,$temp(port)"
  # name already set to "Switch1" etc
  set device_arr($name,$port) $data
  array unset temp

做一个 parray device_arr 给出:

device_arr(Switch1,1) {  port "1" name "RHEL53-Complete1" type "host" ip 10.1.1.10 }
device_arr(Switch1,2) {  port "2" name "Windows-Complete1" type "host" ip 10.1.2.11}
....

要找到 swtich1 端口 2 的 ip,你会

  array set temp $device_array(Switch1,2)
   puts "ip of device 2 is $temp(ip)"
于 2013-08-01T06:48:58.540 回答