逐行执行 tcl 命令
execute tcl commands line by line
我有这样一个文件:
set position {0.50 0.50}
set visibility false
set text {ID: {entity.id}\n Value: {entity.contour_val}}
而且我想做一些类似于 source 的事情,但我只想使用文件句柄。
我目前的尝试是这样的:
proc readArray {fileHandle arrayName} {
upvar $arrayName arr
set cl 0
while {! [eof $fileHandle]} {
set cl [expr "$cl + 1"]
set line [gets $fileHandle]
if [$line eq {}] continue
puts $line
namespace eval ::__esg_priv "
uplevel 1 {*}$line
"
info vars ::__esg_priv::*
foreach varPath [info vars ::__esg_priv::*] {
set varName [string map { ::__esg_priv:: "" } $varPath]
puts "Setting arr($varName) -> [set $varPath]"
set arr($varName) [set $varPath]
}
namespace delete __esg_priv
}
puts "$cl number of lines read"
}
代替上层,我尝试了多种 eval 和引用的组合。
我的问题是,它要么在带有列表的行上失败,要么实际上没有设置变量。
如果预期执行的命令是任何有效代码,正确的方法是什么。
一个额外的问题是如何正确应用错误检查,我还没有尝试过。
致电
后
readArray [open "myFile.tcl" r] arr
我希望
parray arr
类似这样的问题:
arr(position) = 0.50 0.50
arr(text) = ID: {entity.id}\n Value: {entity.contour_val}
arr(visibility) = false
顺便说一句:最后一行包含内部 {},应该将其放入字符串变量中。并且无意将其设为字典。
此代码有效,但仍有一些问题:
proc readArray {fileHandle arrayName} {
upvar $arrayName arr
set cl 0
while {! [eof $fileHandle]} {
incr cl ;# !
set line [gets $fileHandle]
if {$line eq {}} continue ;# !
puts $line
namespace eval ::__esg_priv $line ;# !
foreach varPath [info vars ::__esg_priv::*] {
set varName [string map { ::__esg_priv:: "" } $varPath]
puts "Setting arr($varName) -> [set $varPath]"
set arr($varName) [set $varPath]
}
namespace delete __esg_priv
}
puts "$cl number of lines read"
}
我删除了几行似乎没有必要的行,并稍微更改了一些行。
您不需要 set cl [expr "$cl + 1"]
:incr cl
即可。
if [$line eq {}] continue
将失败,因为 [...]
是命令替换。 if {$line eq {}} continue
(大括号而不是方括号)按照您的意图进行。
除非您正在访问另一个范围内的变量,否则您不需要 uplevel
。 namespace eval ::__esg_priv $line
将计算指定命名空间中的一行。
我没有更改以下内容,但也许您应该:
set varName [string map { ::__esg_priv:: "" } $varPath]
按预期工作,但 set varName [namespace tail $varPath]
更干净。
请注意,如果存在与文件中的其中一个变量同名的全局变量,则不会创建名称空间变量;全局变量将被更新。
如果您打算将 text
变量中的值用作字典,则需要删除 \n
或大括号。
根据你的问题标题,你想逐行评估文件。如果可以取消该要求,则可以通过在一个操作中读取整个脚本然后使用单个 namespace eval
.
对其进行评估来简化您的代码
预计到达时间
这个解决方案更加健壮,因为它在沙箱中读取脚本(在编写将执行任意外部代码的代码时总是一个好主意)并重新定义(在该沙箱内)set
命令在数组中创建成员而不是常规变量。
proc readArray {fileHandle arrayName} {
upvar 1 $arrayName arr
set int [interp create -safe]
$int alias set apply {{name value} {
uplevel 1 [list set arr($name) $value]
}}
$int eval [read $fileHandle]
interp delete $int
}
为了更安全地防止与全局变量等的意外交互,请查看 Tcllib 中的 interp
包。它可以让你创建一个完全空的解释器。
文档:apply, continue, eof, foreach, gets, if, incr, info, interp package, interp, list, namespace, proc, puts, set, string, uplevel, upvar, while
我有这样一个文件:
set position {0.50 0.50}
set visibility false
set text {ID: {entity.id}\n Value: {entity.contour_val}}
而且我想做一些类似于 source 的事情,但我只想使用文件句柄。
我目前的尝试是这样的:
proc readArray {fileHandle arrayName} {
upvar $arrayName arr
set cl 0
while {! [eof $fileHandle]} {
set cl [expr "$cl + 1"]
set line [gets $fileHandle]
if [$line eq {}] continue
puts $line
namespace eval ::__esg_priv "
uplevel 1 {*}$line
"
info vars ::__esg_priv::*
foreach varPath [info vars ::__esg_priv::*] {
set varName [string map { ::__esg_priv:: "" } $varPath]
puts "Setting arr($varName) -> [set $varPath]"
set arr($varName) [set $varPath]
}
namespace delete __esg_priv
}
puts "$cl number of lines read"
}
代替上层,我尝试了多种 eval 和引用的组合。 我的问题是,它要么在带有列表的行上失败,要么实际上没有设置变量。 如果预期执行的命令是任何有效代码,正确的方法是什么。
一个额外的问题是如何正确应用错误检查,我还没有尝试过。
致电
后readArray [open "myFile.tcl" r] arr
我希望
parray arr
类似这样的问题:
arr(position) = 0.50 0.50
arr(text) = ID: {entity.id}\n Value: {entity.contour_val}
arr(visibility) = false
顺便说一句:最后一行包含内部 {},应该将其放入字符串变量中。并且无意将其设为字典。
此代码有效,但仍有一些问题:
proc readArray {fileHandle arrayName} {
upvar $arrayName arr
set cl 0
while {! [eof $fileHandle]} {
incr cl ;# !
set line [gets $fileHandle]
if {$line eq {}} continue ;# !
puts $line
namespace eval ::__esg_priv $line ;# !
foreach varPath [info vars ::__esg_priv::*] {
set varName [string map { ::__esg_priv:: "" } $varPath]
puts "Setting arr($varName) -> [set $varPath]"
set arr($varName) [set $varPath]
}
namespace delete __esg_priv
}
puts "$cl number of lines read"
}
我删除了几行似乎没有必要的行,并稍微更改了一些行。
您不需要 set cl [expr "$cl + 1"]
:incr cl
即可。
if [$line eq {}] continue
将失败,因为 [...]
是命令替换。 if {$line eq {}} continue
(大括号而不是方括号)按照您的意图进行。
除非您正在访问另一个范围内的变量,否则您不需要 uplevel
。 namespace eval ::__esg_priv $line
将计算指定命名空间中的一行。
我没有更改以下内容,但也许您应该:
set varName [string map { ::__esg_priv:: "" } $varPath]
按预期工作,但 set varName [namespace tail $varPath]
更干净。
请注意,如果存在与文件中的其中一个变量同名的全局变量,则不会创建名称空间变量;全局变量将被更新。
如果您打算将 text
变量中的值用作字典,则需要删除 \n
或大括号。
根据你的问题标题,你想逐行评估文件。如果可以取消该要求,则可以通过在一个操作中读取整个脚本然后使用单个 namespace eval
.
预计到达时间
这个解决方案更加健壮,因为它在沙箱中读取脚本(在编写将执行任意外部代码的代码时总是一个好主意)并重新定义(在该沙箱内)set
命令在数组中创建成员而不是常规变量。
proc readArray {fileHandle arrayName} {
upvar 1 $arrayName arr
set int [interp create -safe]
$int alias set apply {{name value} {
uplevel 1 [list set arr($name) $value]
}}
$int eval [read $fileHandle]
interp delete $int
}
为了更安全地防止与全局变量等的意外交互,请查看 Tcllib 中的 interp
包。它可以让你创建一个完全空的解释器。
文档:apply, continue, eof, foreach, gets, if, incr, info, interp package, interp, list, namespace, proc, puts, set, string, uplevel, upvar, while