tcl set数组列表产生重复

时间:2012-11-16 18:22:01

标签: tcl

我正在制作一个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>

如果我的代码块看起来很糟糕,请原谅。我按照指示阅读了它们,但它看起来并不正确。我无法解决它,所以只是发布了。

提前致谢...

3 个答案:

答案 0 :(得分:2)

tcl中的数组不是值。因此,它们的行为不像常规变量。事实上它们就像文件句柄或套接字一样特殊。

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

lappend device_arr mydevice

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

要构建您想要的键值数据结构dict。您可以将dict视为一个特殊列表,其中包含以下格式的偶数元素:{key value key value}。实际上,在引入dict之前,这个数据结构甚至可以在非常旧的tcl版本上工作,因为tcl中的foreach循环可以用来处理键值对。

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

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

lappend device_arr [array get mydevice]

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

答案 1 :(得分:2)

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

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不会删除目标数组中的旧键,因此如果在循环中使用它并且键名不总是相同,则需要每次迭代清除该数组。

答案 2 :(得分: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 port2的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)"