tcl语法快速查询手册

0、教程

https://www.yiibai.com/tcl/tcl_environment.html
或者
https://noyesno.net/page/tcltk/20090719-87.html
或者
https://www.learnfk.com/article-tcl-tk/tk_images
或者
https://www.w3cschool.cn/doc_tcl_tk/tcl_tk-itclcmd-body-htm.html?lang=en
或者
https://blog.csdn.net/xmlbm/article/details/100038912
或者
https://blog.csdn.net/artechtor/article/details/1665930


1、安装


1)在https://www.activestate.com/products/tcl/downloads 网站下载tcl8.6;
2)双击ActiveTcl-8.6.9.8609.2-MSWin32-x64-5ccbd9ac8.exe安装包;
3)安装时提示安装microsoft visual C++ 2015 visual studio update3,下载安装这个文件;
4)安装ActiveTcl-8.6.9.8609.2-MSWin32-x64-5ccbd9ac8.exe安装包;


2、基本语法


1)语句分隔
;或者newline
如果每个命令都使用换行符,则分号不是必须的;
2)注释
内联注释用;#
puts "Hello World!" ;# my first print in Tcl program
使用#符号注释单行
# my first program in Tcl

使用条件为0的if写入多行或块注释,;
if 0 {
my first program in Tcl program
Its very simple
}
3)行继续
\在行尾
% set a [list aa \
bb \
cc ]
aa bb cc
% set a
aa bb cc
4)变量
使用字母、数字、下划线和$符号组成变量、函数名称;
大小写敏感;

5)保留字,不能被用作常量或变量。常见保留字有after、continue、else、catch、file等;
保留字列表:https://www.yiibai.com/tcl/tcl_basic_syntax.html
6)特殊变量,系统内置的变量。常见的有argc、argv、env、tcl_version等;
特殊变量列表:https://www.yiibai.com/tcl/tcl_special_variables.html#article-start
特殊变量 描述
argc 指命令行参数的个数。
argv 指包含命令行参数的列表。
argv0 是指被解释的文件或由调用脚本的名称的文件名。
env 用于表示是环境变量数组元素。
errorCode 为最后的tcl错误的错误代码
errorInfo 为最后Tcl错误的堆栈跟踪错误信息
tcl_interactive 分别将其设置为1和0交互和非交互模式之间切换
tcl_library 用于设置标准Tcl库的位置
tcl_pkgPath 提供一般都安装包的目录列表
tcl_patchLevel zhideshiTcl解释目前的补丁级别
tcl_platform 用于表示使用对象,包括byteOrder,machine,osVersion平台和操作系统数组元素
tcl_precision 指的是精度,即位数转换为浮点数时,字符串保留。默认值是12.
tcl_prompt1 指的是主提示符
tcl_prompt2 指无效的命令二次提示。
tcl_rcFileName 为用户提供了具体的启动文件。
tcl_traceCompile 用于控制字节码编译的跟踪。用0表示无输出,1为概要和2为详细。
tcl_traceExec 用于控制执行的字节码的跟踪。用0表示无输出,1为概要和2为详细。
tcl_version 返回Tcl解释器的最新版本。
7)替换与不替换
变量替换,用来返回变量的值,例如set a 3;puts $a
命令替换,用来返回命令的值,例如puts [expr 1 + 6 + 9]
" string " 带替换的引用
{ string } 不带替换的引用
[ string ] 命令替换
8)tcl命令的语法:commandName argument1 argument2 ... argumentN

 

3、数据类型


1)整数类型


set myVariable 18
puts $myVariable # 此时myVariable自动转为字符串类型,再输出
puts [expr $myVariable + 6 + 8] # 此时myVariable转换为整形,进行数学计算


2)字符串类型


set myVariable hello # 单个单词不需要使用双引号
set myVariable "hello world" # 多个单词需要使用双引号
set s1 "Hello World"
set s2 "o"
读取某个索引的字符
string index "Hello! " 0;#输出H
string index "Hello! " end;#输出!
string index "Hello! " end-4;#输出e
截取字符串
string range "Sample string" 3 7;#输出ple s
string range "Sample string" 3 end;#输出ple string
字符串长度
puts [string length $s1]
大小写转换
puts [string toupper $s1]
puts [string tolower $s1]
去掉字符串两边空格
set s1 " hello world "
set s2 " "
puts [string trimright $s1 $s2];#去掉右空格
puts [string trimleft $s1 $s2];#去掉左空格
puts [string trim $s1 $s2];#去掉两边空格
合并字符串
set s1 "Hello"
append s1 " World"
puts $s1;#输出“Hello World”
【注意:append命令后的变量s1直接使用,不加$符号,这个命令直接修改s1的值】
拆分字符串
puts [split "red_green_blue" _];#输出red green blue
字符串比较
string compare "This is a tcltk example" "This is a TCLTK example";#返回1,因为ASCII码T在t之后,所以从顺序上来说前面的字符串显然在前
string compare "This is a tcltk example" "This is a tcltk example";#返回0,两个字符串相等
string compare "This is a TCLTK example" "This is a tcltk example";#返回-1

string equal "This is a TCLTK example" "This is a tcltk example";#不同返回0
string equal "This is a tcltk example" "This is a tcltk example";#相同返回1
搜索字符串
string first "tcltk" "This is a tcltk example";#返回10,返回第一次匹配的索引,如果找不到则返回-1
string last "is" "This is a tcltk example";#返回5,返回最后一次匹配的索引


替换字符串
string map {key1 value1 key2 value3 ...} 1abcaababcabababc;#将1abcaababcabababc中的key1替换为value1,key2替换为value2

 

3)列表类型


列表是一组元素,使用双引号或者大括号表示简单的列表。
set myVariable {red green blue}
set myVariable "red green blue"

|命令 |说明
| |
|list arg1 arg2 ... |创建一个列表
| |
|lindex list index |返回列表 list 中的第 index 个元素(element)值
| |
|llength list |计算列表 list 元素个数
| |
|lrange list index1 index2 |返回指定范围内(从 index1 到 index2)的元素
| |
|lappend list arg1 arg2 ... |将新元素追加到原来列表 list 后组成新的列表
| |
|linsert list index arg1 arg2 ... |将新元素插入到 list 中位于 index 元素之前的位置上
| |
|lreplace list index1 index2 arg1 arg2 ... |替换指定范围的元素
| |
|lsearch ?mode? list value |根据匹配模式 mode,查找 list 中与 value 匹配的元素位置索引。mode 一般为-exact、-glob 和regexp.默认为-glob。找不到返回-1。
| |
|lsort ?switches? list |根据开关选项对列表进行排序
| |
|concat list1 list2 ... |连接多个列表内容成一个列表
| |
|join list joinChars |以 joinChars 为分隔符将列表中的元素合并在一起
| |
|split string splitChars |以 splitChars 中的字符作为分隔符将字符串分解为列表元素。
| |
|foreach var list {proc body} |遍历列表各项,逐次将各元素值存入 var 中并执行 proc body。相当于一个循环控制语句。

list命令:用来创建列表。一个列表可以包含子列表,即列表可以嵌套。
set l1 [list Sun Mon Tues]
结果=>Sun Mon Tues

set l2 [list $l1 Wed] ;#列表 l1 含有三个元素
结果=> {Sun Mon Tues} Wed

set str1 "Sun Mon Tues"
结果=>Sun Mon Tues

set l2 [list $str1 Wed] ;#列表 l2 中含有两个元素。第一个元素用花括号括起来。
结果=>{Sun Mon Tues} Wed ;#和上面的命令结果相同:“列表是特殊的字符串”。

concat命令:以空格为分隔符将多个列表拼装在一起形成新的列表。它和双引号的作用比较相似。
set x {1 2}
结果=> 1 2

set y "$x 3 " ; #$x 被替换后,作为列表结构的花括号被去掉,
结果=> 1 2 3 ; #元素被提出来和 3 一起作为新列表的元素

set y "$x {3}"
结果=> 1 2 {3}

set y [concat $x 3] ;#结果同上面的双引号
结果=> 1 2 3

set y [concat $x {3}]
结果=> 1 2 3

lindex命令:返回列表中指定位置的特定元素。列表索引从 0 开始记数!
set x { 1 2 3 }
结果=> 1 2 3

puts [lindex $x 1]
=>2

llength命令:可以获得一个列表内元素的个数。
set length "1 2 3 4 5"
结果=>1 2 3 4 5 ;#定义了一个字符串
set num [llength $l1] ;#这里 l1 被看作列表了
=>5

lrange命令:返回一个指定区段的列表元素,可以以 end 或者 end-n 作为索引(n 为正整数)。
lrange {1 2 3 {4 5} 6} 2 end
结果=> 3 {4 5} 6

lmap命令:在最简单的情况下,只有一个循环变量varname,和一个列表list,list中的值会依次分配给变量varname,body是一个Tcl脚本。
lmap varname list body
body所代表的脚本会使用varname进行处理。
如果主体的执行正常完成,则body的结果追加到累加器列表中,lmap最后返回累加器列表。
如果body中最后一个命令返回值为空,则lmap的返回结果为所有值均为空值的列表。

linsert命令:用来将元素插入到一个列表的由索引指定的位置。如果索引为 0 或者更小,则元素就会被添加到最前面。
如果索引值大于或者等于列表长度,则元素被追加到列表尾部。其他情况元素被添加到指定位置之前。
注意:这个操作不会改变原来列表的内容,而是返回一个新列表。
set x {1 2}
结果=>1 2

set new [linsert $x 0 he she]
结果=>he she 1 2

set new [linsert $x end he she]
结果=>1 2 he she

set new [linsert $x 1 he she]
结果=>1 he she 2

puts $x
结果=>1 2 ;# x 的值没有改变

puts $new
结果=>1 he she 2

lreplace命令:将一个指定区段的列表元素替换为新元素,
如果没有指定新元素,则这个区域的元素就会被从列表中删除。
注意:这个操作不会改变原来列表的内容,而是返回一个新列表。
set new {1 he she 2}
结果=>1 he she 2

set y [lreplace $new 1 2 B C]
结果=>1 B C 2

set y [lreplace $new 0 0]
结果=>he she 2 ;#删除列表元素,将第 0 个元素删除

puts $new
结果=>1 he she 2 ;#new 的内容并没有改变,这和 string replace 相同。

set y [lreplace $new 1 2]
结果=>1 2 ;#将第 1、2 个元素删除

lsort命令:实现对列表的排序。
注意:排序操作不影响原表,而是返回排序之后的新表。
set list "a Z z n100 n200 M p Hl hL m 1 20"
结果=>a Z z n100 n200 M p Hl hL m 1 20

lsort -ascii $list
结果=>1 20 Hl M Z a hL m n100 n200 p z

lsort -dictionary $list
结果=>1 20 a Hl hL M m n100 n200 p Z z

lsearch命令:与in命令类似。在给定列表中搜索与匹配字符串匹配的元素,成功就返回正确的元素索引,否则返回-1。
lsearch 支持通配符格式,但可以使用-exact 选项将其屏蔽而进行精确匹配。
set l1 [list This is one list]
结果=> This is one list

set index [lsearch $l1 l*]
结果=> 3

set index [lsearch -exact $l1 l*]
结果=>-1

set index [lsearch -exact $l1 list]
结果=>3

set stripped [lsearch -inline -all -not -exact $inputList $elemToRemove];#删除元素的另一种方法是将其过滤掉

-all 返回全部匹配项
-inline 返回匹配项的值,而不是索引

concat命令:将连接多个列表
concat a b {c d e} {f {g h}}
将返回结果“a b c d e f {g h}”)
in命令:判断列表中是否有某个字符串
if {orange in $var} {
puts "orange is in var"
}

join命令:接收一个列表,并用指定的分隔符将列表元素整合成一个字符串
join {1 {2 3} {4 5 6}} :
结果=> 1:2 3:4 5 6

split命令:作用与 join 的作用相反,它接收一个字符串,并根据给定的字符将其分割转换成
注意:这个操作不会改变原来字符串的内容,而是返回一个新列表。
set str cm8/auto/tools/aries/ASAM/NE/SNMP/IMPL/ne_create_board.tcl
结果=>cm8/auto/tools/aries/ASAM/NE/SNMP/IMPL/ne_create_board.tcl
set s /
结果=>/

set l1 [split $str $s]
结果=> cm8 auto tools aries ASAM NE SNMP IMPL ne_create_board.tcl

set l2 [split $str "/."] ;#可以指定多个分割符
结果=> cm8 auto tools aries ASAM NE SNMP IMPL ne_create_board tcl

foreach命令:会遍历整个列表,逐次取出列表的每个元素的值放到指定变量中,使用者可以在跟随的过程体中添加必要的处理过程。
set lst "1 2 3 4"
结果是=>1 2 3 4

foreach item $lst {
puts $item
}
结果是=>
1
2
3
4

lappend命令:追加项目到列表末尾
set var orange
lappend var "red"
puts $var;#输出orange red
【注意:lappend命令后的变量s1直接使用,不加$符号,这个命令直接修改s1的值】
lreverse命令:反转列表元素的顺序
lreverse {a a b c}
-->c b a a
lreverse {a b {c d} e f}
-->f e {c d} b a
用列表项给变量赋值
set var {orange blue red green}
lassign $var colour1 colour2
puts $colour1;#输出orange
puts $colour2;#输出blue

按照索引删除列表元素:参考replace命令
按照值删除列表元素:参考lsearch命令


4)数组(array)


set marks(english) 80
puts $marks(english)
set marks(mathematics) 90
puts $marks(mathematics)
% array get marks math
math 90
数组的大小
puts [array size marks]
【注意:数组size命令后的变量名不加$符号】
数组的索引
puts [array names marks]
【注意:数组names命令后的变量名不加$符号】
使用foreach语句遍历数组
foreach index [array names marks] {
puts "marks($index): $marks($index)"
}
# 上面的index是临时定义的变量


5)布尔类型


1,yes 或 true 为真值
0,no 或 false 为假值


6)字典


https://webcache.googleusercontent.com/search?q=cache:ZGvZvFZG-FkJ:https://blog.csdn.net/asty9000/article/details/90217533+&cd=1&hl=zh-CN&ct=clnk
创建字典
set colours [dict create colour3 "black" colour4 "white"]
puts $colours;#输出colour3 black colour4 white
修改或者新增键值对
dict set colours colour1 red
dict set colours colour3 green
puts $colours;#输出colour3 green colour4 white colour1 red
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict set dic f g h 8
a 1 b 2 c 3 f {g {h 8}}
【注:set命令只能每次修改一个键值对】
unset
dict unset dictName key ?key ...?
删除字典dictName中key对应的值。
如果dictName中不存在key,则不进行任何操作。
当存在多个键时表示删除嵌套字典中的元素,此时的键路径上除了最后一个key可以不存在外,
前面的key必须存在,否则会报错key "xxx" not known in dictionary。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict unset dic c
a 1 b 2
remove
dict remove dictValue ?key ...?
返回一个新的字典【不改变原来字典】,字典包括键为key以外的所有dictValue中的键值对。
如果不指定任何键则返回的字典与dictValue相同,如果key不在dictValue中,则不进行任何操作。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict remove $dic a
b 2 c 3
% puts [dict keys $dic]
a b c
merge
dict merge ?dictValue ...?
返回一个新的字典,包含dictValue中的所有元素,多个dictValue具有相同的key的元素的时,
最后一次出现的key对应的值会出现在新的字典中。
% set newDic [dict merge {a 1 b 2} {c 3 d 4 b 5}]
a 1 b 5 c 3 d 4
字典大小
set colours [dict create colour3 black colour4 white]
puts [dict size $colours];#输出2
字典某个键的值
set colours [dict create colour3 black colour4 white]
puts [dict get $colours colour3];#输出black
字典中的所有键
set colours [dict create colour3 black colour4 white]
set keys [dict keys $colours]
puts $keys;#输出colour3 colour4
字典中的所有值
set colours [dict create colour3 black colour4 white]
set values [dict values $colours]
puts $values;#输出black white
字典是否含有某个键
set colours [dict create colour3 black colour4 white]
set result [dict exists $colours colour3]
puts $result;#输出1
字典迭代
set colours [dict create colour3 black colour4 white]
foreach key [dict keys $colours] {
puts [dict get $colours $key]
}
# 分两行分别输出black和white
字典遍历列表
用于遍历字典dictValue,在执行body中脚本时,字典中每个键值对的键和值会分别分配给名为指定的keyName和valueName变量。
dict for命令的结果为空字符串。当在body执行过程中遇到break时,会中止循环,结束dict for命令。
当遇到continue命令时,会结束本轮循环。字典按键值对的插入顺序进行遍历。
set colours "colour1 red colour2 white colour3 black"
dict for {key value} $colours {
puts "key:$key,value:$value"
}
append
dict append dictName key ?string ...?
将给定的string追加到字典dictName中键key的值后面。
如果key不存在,则相当于新增一个key的映射。
如果未指定string,则视为空字符串。
多个string会合并为一个字符串。此命令返回更新后的字典。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
%
% dict append dic ab 4 5
a 1 b 2 c 3 ab 45
% dict append dic a 6
a 16 b 2 c 3 ab 45
%
% dict append dic ab
a 16 b 2 c 3 ab 45

lappend
dict lappend dictName key ?value ...?
将给定的value追加到dictName中指定key对应的列表中,可以同时追加多个value,
如果key对应的值不是列表,会创建一个列表,并将原有值和追加的值放到列表中。
如果key存在,但没有指定value,则不做任何操作。
如果dictName中不存在key,则视为空列表,若此时value也未指定则会添加一个键为key值为空列表的元素,
若指定了value则会将value作为列表的元素。如果key对应的值不能表示为列表则会报错。
% dict lappend dic a
a 1 b 2 c 3
% dict lappend dic a 1
a {1 1} b 2 c 3
% dict lappend dic e
a {1 1} b 2 c 3 e {}
% dict lappend dic e 1 2 3 4 5
a {1 1} b 2 c 3 e {1 2 3 4 5}
incr
dict incr dictName key ?increment?
将字典dictName中指定key对应的值增加increment。
如果未指定increment,则默认增加1。
如果不存在指定的key,则视为0。如果key对应的值不是整数则会报错
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict incr dic a
a 2 b 2 c 3

dict update dictName key varName ?key varName ...? body
使用映射dictName中key对应的值的变量varName执行body中的脚本。
如果dictName没有指定的key,则其对应的变量varname为未设置。
当body终止时,对varName所做的更改都会被会写到字典内即使body的结果是一个错误或其他异常,
除非字典不可读所有的更新都被静默的丢弃。
除非发成异常,否则dict update的结果是body的执行结果。
建议只在本地作用域内使用此命令,因为dict update命令中的变量varName在命令完成后,除非显示的执行unset,否则仍然存在。
此外,只有当body终止时,才会更改dictName中的内容
replace
dict replace dictValue ?key value ...?
返回一个新的字典,包括传入的dictValue以及一些额外的或不同的键值对。
如果不传如键值对key、value,则返回的新字典与dictValue相同。键值对必须是成对出现,否则会报错。
dictValue中存在的key会用心的value替换,不存在的key会新增到新的字典。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% set newDic [dict replace $dic a 1 b 3 d 4]
a 1 b 3 c 3 d 4


info
dict info dictValue
返回字典信息,信息的具体格式取决于字典的实现方式。由于字典是散列表实现的,所以放回结果类似于array statistics命令。
with
dict with dictName ?key ...? body
与update类似,使用dictName中key对应的值执行body中的脚本。
如果key为多个表示嵌套字典的键路径,此时会使用键路径对应的字典执行body中的脚本。
字典中key对应的值会映射到与key同名的变量中。
与dict update一样,如果dictName不可读或者字典调整导致键路径没有对应的值,则会静默丢弃字典的更新。
除非发成异常,否则dict with的结果是body的执行结果。
建议只在本地作用域内使用此命令,因为dict with命令中的变量在命令完成后,除非显示的执行unset,否则仍然存在。
此外,只有当body终止时,才会更改dictName中的内容。当body终止时如果dictName不是字典则会产生错误。
因此当字典中的键与字典变量dictName有冲突时不建议使用此命令。
% set info [dict create forenames Joe surname Schmoe street {147 Short Street} city Springfield phone 555-1234]
forenames Joe surname Schmoe street {147 Short Street} city Springfield phone 555-1234
% dict with info {
puts " Name: $forenames $surname"
puts " Address: $street, $city"
puts " Telephone: $phone"
}
Name: Joe Schmoe
Address: 147 Short Street, Springfield
Telephone: 555-1234
filter
dict filter dictValue filterType arg ?arg ...?
返回一个新的与指定过滤类型匹配的键值对的字典
有三种过滤方式:按照key过滤,按照value过滤,或者使用脚本过滤
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict filter $dic key a*
a 1
% dict filter $dic value 2*
b 2
% dict filter $dic script {k v} { expr { $k < "e"} }
a 1 b 2 c 3
map
dict map {keyName valueName} dictValue body
Tcl8.6新增的命令。
此命令通过执行body中的命令,对字典dictValue中的键值进行转换,然后返回一个新的包含转换后的键值对的字典。
名为keyName、valueName对应于dictValue中键值对的键与值。
每次body执行完后,变量keyName、valueName的值即为新字典的键值。
当在body执行过程中遇到break时,会中止循环,结束dict map命令。
当遇到continue命令时,会结束本轮循环。
另外要注意的是,如果不想对值做改变,需要对其进行显示的set操作,否则值会被设置为body中最后一个命令的返回值。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict map { k v } $dic { set k $k$k; set v $v;}
aa 1 bb 2 cc 3

5)句柄
句柄通常用于文件、网络请求的处理
set myfile [open "filename" r]


4、数学计算


1)expr用于表示数学表达式,默认精度为12位数;
2)使用tcl_precision特殊变量来更改精度;


5、if语句


if { $a == 10 } {
puts "Value of a is 10"
} elseif { $a == 20 } {
puts "Value of a is 20"
} else {
puts "Exact value of a is: $a"
}
# 注意:else和两边的花括号间有空格。

TCL中有4个用于表达式的字符串操作符:eq、ne、in、ni.
1)eq和ne用来检查字符串是否相等,如果相等eq返回1,ne返回0,如果不等ne返回1,eq返回0.
2)除了检查字符串相等还是不相等的eq和ne操作符,<、>、<=、>=、==、!=操作符都可以进行字符串比较
3)如果指定字符串是列表的元素,in操作符返回1,否则返回0.如果字符串不是列表的元素,in操作符返回1,否则返回0


6、switch语句


set grade B;

switch $grade {
A {
puts "Well done!"
}
B {
puts "Excellent!"
}
default {
puts "Invalid grade"
}
}
puts "Your grade is $grade"


7、while循环


set a 10
while { $a < 20} {
puts "value of a is $a"
incr a;#自增1。也可以指定步长,例如-1,此时每次自减1。
}


8、for循环


for { set a 10} { $a < 20 } { incr a } {
puts "value of a is $a"
}


9、过程(也就是函数)


1)无参数过程
proc hello {} {
puts "hello world!"
}
hello;#输出hello world!
2)多个参数过程
proc add { a b } {
puts [ expr $a + $b ]
}
puts [add 4 5];#输出9
3)可变参数过程
proc sum { numbers } {
set result 0
foreach number $numbers {
set result [ expr $result + $number ]
}
return $result
【注意:这里要返回值,不是返回变量】
}
puts [ sum {1 2 3 4 5} ]
4)默认参数
proc add { a {b 100}} {
puts [expr $a + $b ]
}
puts [ add 15 ]
5)递归
proc factorial { a } {
if { $a == 1} {
return 1
}
return [expr $a*[factorial [ expr $a - 1 ]]]
}
puts [factorial 3]


10、命名空间


10.1、创建命名空间


namespace eval MyMath {
variable myResult
}
proc ::MyMath::add { a b } {
set ::MyMath::myResult [ expr $a + $b ]
}
::MyMath::add 5 6
puts $::MyMath::myResult


10.2、命名空间嵌套


namespace eval MyMath {
variable myResult
}
namespace eval extendedMath {
namespace eval MyMath {
variable myResult
}
}
set ::MyMath::myResult "test1"
puts $::MyMath::myResult
set ::extendedMath::MyMath::myResult "test2"
puts $::extendedMath::MyMath::myResult


10.3、导入导出命名空间(不必借助空间名字,直接引用空间内的变量和过程)


namespace eval MyMath {
variable myResult
namespace export Add
}
proc ::MyMath::Add {a b } {
return [expr $a + $b ]
}
namespace import ::MyMath::*
puts [Add 3 4 ]


10.4、忘记命名空间


namespace eval MyMath {
variable myResult
namespace export Add
}
proc ::MyMath::Add {a b } {
return [expr $a + $b ]
}
namespace import ::MyMath::*
puts [Add 3 4 ]
namespace forget ::MyMath::*
puts [Add 4 5 ];#因为忘记了命名空间,所以这句会报错


10.5、uplevel与upvar


10.5.1、命名空间层级
proc foobar {} { puts "foobar:\t[info level]"; foo; };
proc foo {} { puts "foo:\t[info level]"; bar; };
proc bar {} { puts "bar:\t[info level]" };
puts "global:\t[info level]"
foobar
上面脚本输出如下
global: 0
foobar: 1
foo: 2
bar: 3
由此,可以看出最外层空间编号是0,多层函数调用时,空间编号依次增长。
10.5.2、uplevel:提升proc层级,去变量所在空间层级,使用外层空间变量
namespace eval foo {
namespace export bar
proc bar {} {
uplevel 1 { set name "Kid"; };#数字1表示相对层级,数字加上井号表示绝对层级
set name "blah...";
}
}
set name "Rex Tsai"
puts $name;输出Rex Tsai
foo::bar
puts $name;输出Kid
10.5.3、upvar
upvar - 创建链接到外层空间的变量
用法:upvar ?level? otherVar myVar ?otherVar myVar ...?
level,可选,指空间层级,默认为1,表示链接到外1层空间的变量
otherVar,外层空间的变量名
myVar,链接的名字
令myVar和外层空间的otherVar指向同一个内存地址,myVar与otherVar变量值相同,修改myVar等同于修改otherVar
如果外层空间中otherVar不存在,则不会创建myVar.

例一:
upvar:提升proc内的var,链接到外层空间变量,使用外层空间变量
namespace eval foo {
namespace export bar
proc bar {} {
upvar 1 testname j # 或是 upvar #0 testname j
set j "Kid"
}
}
set testname "Rex Tsai"
puts $testname;输出Rex Tsai
foo::bar
puts $testname;输出Kid

例二:
set a 1 ;#定义变量a, 并且值设为1

proc test {b} {
upvar $b mya

puts $b

puts $mya

}

test a ;#调用函数 test

a ;#参数b的值为a(变量名)

1 ;#由于upvar 使mya(变量名)指向a(变量名)指向的同一个变量,mya的为a的值

upvar使的在函数内部可以更改函数外部的变量的值
10.5.4、uplevel与upvar是通过不同方式使用外层空间变量
upvar 与 uplevel 都必须指定你想使用的命名空间层级, 如果不指定, 他会使用上一层, 默认值是 1.
另外, 也可以使用抽象的层级号码, 他的方式是以 #开头, 后面接一个层级数字
10.5.5、namespace upvar:通过一个变量链接,使用其它空间的变量
namespace upvar ::__script load script_load
load是::__script空间内的变量
可以通过script_load引用load


10.6、删除命名空间


namespace delete MyMath


10.7、取得限定名称的命名空间部分或者尾部命名空间


10.7.1、取得头部的命名空间部分
% namespace qualifiers China::SiChuan
China
% namespace qualifiers China::SiChuan::Chengdu
China::SiChuan
10.7.2、取得尾部的命名空间
% namespace tail China::SiChuan
SiChuan
% namespace tail China::SiChuan::Chengdu
Chengdu
10.8、获取空间名称
namespace current命令: --> 获取当前命名空间的名称
namespace parent命令: --> 获取当前命名空间的父命名空间
namespace childred命令: --> 获取当前命名空间的子命名空间

11、tcl包、库、引用文件


1)在文件new.tcl中使用source test.tcl命令后,可以在new.tcl中直接使用定义在test.tcl中的过程。

 

12、文件IO


tcl内置的处理文件的命令:open打开,read读全部,puts写,gets读一行,close关闭文件。
read例子
set fp [open "input.txt" w]
puts $fp "test"
close $fp
set fp [open "input.txt" r]
set data [read $fp]
puts $data
close $fp
gets例子
set fp [open "input.txt" w]
puts $fp "test1\ntest2"
close $fp
set fp [open "input.txt" r]
while { [gets $fp data] >= 0 } {
puts $data
}
close $fp


13、错误处理


catch script ?resultVarName? ?optionsVarName?
如果脚本产生一个错误,catch将返回一个非0的整数,
如果给出了resultVarName变量,当返回1时,存储在resultVarName中的为错误信息,如果返回0,存储在resultVarName中的为脚本运行结果。
1)用catch捕获自定义error
proc Div { a b } {
if { $b == 0 } {
error "this is error message" "this is error info" 401
} else {
return [expr $a / $b]
}
}
# 注意:被catch的脚本应该用{}包起来。
if { [catch {Div 10 0} errorMsg]} {
puts "Error Message: $errorMsg"
puts "Error Info: $errorInfo"
puts "Error Code: $errorCode"
}

2)用catch捕获error


14、tcl内置函数


内置函数列表:https://www.yiibai.com/tcl/tcl_builtin_functions.html
1)数学函数
namespace import ::tcl::mathfunc::*
puts [abs -5];#绝对值
puts [ceil 5.2];#向上取整
puts [floor 5.4];#向下取整
puts [sin [asin 1]];#计算反正弦和正弦值
puts [double 12];#转为浮点数
puts [fmod 5 2];#5/2的余数
puts [int 4.5];#取整
puts [pow 4 2];#乘方
puts [rand];#0~1间的随机数
puts [round 4.6];#四舍五入取整
puts [sqrt 16];#开方
2)时间日期
# get seconds
set currentTime [clock seconds]
puts $currentTime
# get format
puts "the time is : [clock format $currentTime -format %H%M%S]"
puts "the date is : [clock format $currentTime -format %D]"
# 字符串转为秒数
set date "Jun 15, 2014"
puts [clock scan $date -format {%b %d, %Y}]

set date "2020-12-28 18:43:42"
set date [clock scan $date -format {%Y-%m-%d %H:%M:%S}]
puts $date
3)执行系统命令
puts [exec ping 127.0.0.1]
puts [exec ipconfig]


15、正则表达式


1、基本格式
关键字 表达式 字符串 匹配值 分组1 分组2 ...
regexp {([A-Z,a-z]*).([A-Z,a-z]*)} "Tcl Tutorial" a b c
puts "Full Match: $a";#输出"Tcl Tutorial"
puts "Sub Match1: $b";#输出"Tcl"
puts "Sub Match2: $c";#输出"Tutorial"
2、常用参数
-nocase:忽略大小写
-line:新行敏感,换行后忽略字符。
-start index:搜获模式开始设置偏移。
3、常见用法
通常,我们并不关心匹配值,只关心分组,使用->存储匹配值,格式如下
regexp $exp $string -> sub1 sub2


16、文件操作


文件测试
file exists $name ; # 判断文件是否**存在**
file isdirectory $name ; # 判断文件是否是**目录**
file isfile $name ; # 判断文件是否是**文件**

file readable $name ; # 判断文件是否**可读**
file writable $name ; # 判断文件是否**可写**
file executable $name ; # 判断文件是否**可执行**
file owned $name

文件路径、文件名
set name /path/a/b/c.txt

file extension $name ; # .txt
file dirname $name ; # /path/a/b
file rootname $name ; # /path/a/b/c
file tail $name ; # c.txt

file nativename $name ; # 系统默认格式的文件名

文件信息
set name /path/a/b/c.txt

file size $name ; # 单位 字节(bytes)
file stat $name finfo
array get finfo * ; # {atime, ctime, dev, gid, ino, mode, mtime, nlink, size, type, uid}

file separator ; # 系统支持的文件路径分隔符

文件操作
file mkdir $name ; # mkdir -p $name 创建目录,自动创建父目录

file copy $source $target ; # copy 文件: cp $source $target

file delete $path ; # 删除文件 rm $path
file delete --force $path ; # 强制删除非空目录 rm -rf $path

file rename $source $target ; # mv $source $target

获取链接(link)的最终目标文件
在copy操作中,如果遇到 link,而link的目标文件又是相对路径的时候,就需要找到link所最终指向的文件。

proc file_read_link {file} {
while {[file type $file] == "link"} {
set file [file join [file dirname $file] [file readlink $file]]
}
return $file
}
巧妙地利用了 file join 的特性省略了对目标文件是相对路径还是全路径的判断。

 

17、格式化format


字符 说明
d 有符号整数
u 无符号整数
i 有符号整数。变元可以是十六进制(0x)或八进制(0)格式
o 无符号八进制数
x 或X 无符号十六进制数
c 将整数映射到对应的 ASCII 字符
s 字符串
f 浮点数
e 或E 科学记数法表示的浮点数
g 或G 以%f 或%e 格式(要短一些)来表示的浮点数
- 使字段左对齐
+ 字段右对齐
space 在数字前加一个空格,除非数字带有前导符号。这在将许多数字排列在一起时非常有用
0 使用 0 作为补白
# 前导 0 表示八进制,前导 0x 表示十六进制数。浮点数中总要带上小数点。不删除末尾的 0(%g)

例一
#要取第 2 个变元值,即 5。位置说明符的格式为 2$,并用\来引用符号$:
%set res [format "%2\$s" 1 5 9]
=>5
%puts $res
=>5
%set str [format "%3\$s %1\$s %2\$s" "are" "right" "You"]
=> You are right

例二
%format "%x" 20
=>14 ;# 将 20 转换为十六进制数
%format "%8x" 20
=> 14 ;# 将 20 转换为十六进制数,8 位数据宽度,右对齐
%format "%08x" 20
=>00000014 ;#与上一命令相似,但用 0 添齐
%format "%-8x" 20
=>14 ;#宽度 8 位,左对齐
%format "%#08x" 20
=>0x000014

 

18、特殊语法


1){*}
功能:将包含多个元素的列表视为多个值,而不是一个值。
举例:
% set list1 [list 1 2 3]
>>1 2 3
% set list2 [list a b c]
>>a b c
% set concatenation [list {*}$list1 {*}$list2]
>>1 2 3 a b c
% set concatenation [list $list1 $list2]
>>{1 2 3} {a b c}
2)args
功能:当args是最后一个参数时,它是一个列表,由剩余的全部传入参数对它赋值。
举例:
%proc demo {first {second "none"} args} {
puts "first = $first"
puts "second = $second"
puts "args = $args"
}
% demo one two three four five
>>first = one
>>second = two
>>args = three four five
3)subst
功能:通常用于花括号{}内部的反斜线、命令和变量替换
语法:subst ? -nobackslashes? ? -nocommands? ? -novariables? string
描述:这个命令对 string变元执行变量、命令和反斜杠替换,然后返回替换后的结果,替换的方式和执行Tcl命令的方式相同, string变元被替换两次,一次为Tcl命令的剖析器,另外一次为subst命令。
如果指定了-nobackslashes、-nocommands或-novariables标志位,那么相应的替换将不会执行,比如如果指定了-nocommands标志位,命令替换就不会发生,中括号被当作为普通字符处理。

举例:
set body {{"oldPsw":"$old_passwd","newPsw":"$new_passwd"}}
set body [subst -nocommands -nobackslashes $body]


19、类与实例(面向对象)


这里介绍基于TclOO的类与实例。
1)声明一个类
oo::class create MyExampleClass {
# 关键字constructor,类实例化时调用这个方法
constructor {args} {# like [proc]
# Do something with the args...
}
# 公共方法用关键字method定义,方法名必须是小写字母开头
method foo {bar boo} {# like [proc]
puts "bar=$bar, boo=$boo"
}
# 特殊的公共方法
# 当调用未定义的方法时,会转为调用unknown方法。
method unknown {args} {
puts "unknown is ok"
}
# 通过关键字detroy调用destructor方法
destructor {# No arguments
# Clean up here if necessary
}
}
2)类实例化
# 实例化时指定一个名称
MyExampleClass create exampleObj someArgumentToConstructor...
exampleObj foo 1 2; # prints 'bar=1, boo=2'
exampleObj destroy; # wipes the object out
# 实例化时,赋值给变量
set obj [MyExampleClass new]; # 'Unnamed' object
$obj foo 3 4; # prints 'bar=3, boo=4'
$obj destroy
3)定义实例变量
oo::class create Example2 {
constructor x {
variable X $x
}
method addToX y {
variable X
set X [expr {$X + $y}]
return
}
destructor {
variable X
puts "X is $X"
}
}
Example2 create eg 3
eg addToX 5
eg addToX 2
eg destroy; # Prints 'X is 10'
4)私有方法
# 私有方法名字不以小写字母开头。
# 通过关键字my调用私有方法。
...
method ThePrivateMethod ... ...
method aPublicMethod ... {
puts "before the private call"
my ThePrivateMethod ...
puts "after the private call"
}
...
5)修改类
# 先定义类的名字。
# 再通过oo::define添加类内部的方法。
oo::class create Example2a
# One syntax style: single argument is definition script
oo::define Example2a {
constructor x {
variable X $x
}
destructor {
variable X
puts "X is $X"
}
}
Example2a create foo 3
# Other syntax style: multiple arguments give a single definition
oo::define Example2a method addToX y {
variable X
set X [expr {$X + $y}]
return
}
6)类的继承
# 定义父类
oo::class create fruit {
method eat {} {
puts "yummy!"
}
}
# 定义子类
oo::class create banana {
# 继承父类
superclass fruit
constructor {} {
my variable peeled
set peeled 0
}
method peel {} {
my variable peeled
set peeled 1
puts "skin now off"
}
method edible? {} {
my variable peeled
return $peeled
}
# 修改继承自父类的方法
method eat {} {
if {![my edible?]} {
my peel
}
next
}
}
set b [banana new]
$b eat → prints "skin now off" and "yummy!"
fruit destroy
$b eat → error "unknown command"
7)self
详见:https://www.tcl-lang.org/man/tcl/TclCmd/self.htm

[self]:返回当前实例的信息
oo::class create c {
method foo {} {
puts "this is the [self] object"
}
}
c create a
c create b
a foo → prints "this is the ::a object"
b foo → prints "this is the ::b object"
8)next和nextto
详见:https://www.tcl-lang.org/man/tcl/TclCmd/next.htm
用法1:调用类或者父类中的同名方法
oo::class create theSuperclass {
# 父类中的example方法
method example {args} {
puts "in the superclass, args = $args"
}
}
oo::class create theSubclass {
superclass theSuperclass
# 子类中的example方法
method example {args} {
puts "before chaining from subclass, args = $args"
next a {*}$args b
next pureSynthesis
puts "after chaining from subclass"
}
}
theSubclass create obj
# 实例中的example方法
oo::objdefine obj method example args {
puts "per-object method, args = $args"
next x {*}$args y
next
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# 调用example方法时,会依次调用实例、类、父类中的example方法
obj example 1 2 3
# 打印如下
per-object method, args = 1 2 3
before chaining from subclass, args = x 1 2 3 y
in the superclass, args = a x 1 2 3 y b
in the superclass, args = pureSynthesis
after chaining from subclass
before chaining from subclass, args =
in the superclass, args = a b
in the superclass, args = pureSynthesis
after chaining from subclass

应用一:
[self next]:判断父类中有无当前方法
nextto和
oo::class create fruit {
method eat {} {
puts "yummy!"
}
method myeat {} {
my eat
}
}
# 定义子类
oo::class create banana {
# 继承父类
superclass fruit
constructor {} {
my variable peeled
set peeled 0
}
method peel {} {

if {[self next] ne {}} {
puts "has_next"
puts [self next]
} else {
puts "not has"
}

}
# 修改继承自父类的方法
method eat {} {
if {[self next] ne {}} {
puts "has_next"
puts [self next]
} else {
puts "not has"
}
}
}
set b [banana new]
$b peel;#输出not has
$b eat;#输出has_next
9)oo::class
The oo::class class is the class of all classes;every class is an instance of this class, which is consequently an instance of itself.
每一个类都是oo::class的实例。

This class is a subclass of oo::object, so every class is also an object.
oo::class是oo::object的子类,所以每个类都是一个对象。

10)info object isa object用于测试特定单词是否指向对象,即变量是否存在
% info object isa object abcde
0
% oo::object create abcde
::abcde
% info object isa object abcde
1
% abcde destroy
% info object isa object abcde
0

info object isa category object ?arg?
This tests whether object (a TclOO object) is a member of the given category (possibly as modified by arg). The supported categories are:

info object isa class object
Is object a class? (Being a class means being an instance of oo::class or one of its subclasses.)

info object isa metaclass object
Is object a metaclass? (A metaclass is a subclass of oo::class.)

info object isa mixin object class
Is class directly mixed into object?

info object isa object command
Is command really an object? (If it is, it will be an instance of oo::object or any of its subclasses; that's the whole TclOO class system though.)

info object isa typeof object class
Is object of the type of the given class? (It is if it is an instance of the class or a one of its direct or indirect subclasses.)
11)类对象自己的方法,类似于静态方法概念
CLASS_DEFINE AUTOGUI {
superclass OBJ
self {
method class_init {} {
}
}
}


20、twapi

 

1)最小化指定应用的窗口
package require twapi
if {$argc != 1} {
puts stderr "Usage: [info nameofexecutable] $argv0 APPNAME"
puts stderr "Example: [info nameofexecutable] $argv0 notepad"
exit 1
}

set appname [lindex $argv 0]
# Add an extension if none specified
if {[file extension $appname] == ""} {
append appname .*
}

# Get all pids with that app name
set pids [twapi::get_process_ids -glob -name $appname]

# Only minimize if they are marked as visible. This is important
# else hidden windows will be placed on the taskbar as icons
foreach hwin [twapi::find_windows -pids $pids -visible true] {
twapi::minimize_window $hwin
}

2)向指定窗口发送文本信息
package require twapi
if {$argc != 2} {
puts stderr "Usage: [info nameofexecutable] $argv0 WINDOWTITLE TEXT"
exit 1
}

set title [lindex $argv 0]
set data [lindex $argv 1]
# Get all windows with that title
set windows [twapi::find_windows -text $title]
if {[llength $windows]} {
set win [lindex $windows 0]
# Set the focus to the window
twapi::set_focus $win
# Feed data into the input queue
twapi::send_input_text $data
} else {
puts stderr "No windows found with title '$title'"
}

 

20、socket

 

20.1、socket 服务端开启
格式:socket –server command ?options? port
-server :表明开启的是服务器端
port:端口
command:当有客户端来连接的时候,执行这个过程,这个过程有三个参数
channel:给新客户端的通道
address:提供给客户端连接的 ip 地址
port:端口

20.2、客户端连接服务器端
格式:socket ?options? host port
host port :客户端连接的服务器 ip和端口

20.3、fileevent定义了一个句柄,满足条件时执行
格式:fileevent channelId readable? script?
fileevent channelId writeable? script?
readable:当通道 channelId 有数据准备好被读了,执行脚本 script
writeable:当通道 channelId 有数据准备好接收数据了,执行脚本 script

20.4、vwait 命令使执行暂停,直到 varName 被赋值,即便赋值前后相同
格式:vwait varName


例子一:简单的客户端连接服务端
-----------------------------------------------------------------------------------------
server:
proc accept {chan addr port} {
puts "$addr:$port says [gets $chan]"
puts $chan goodbye;#向通道发送goodbye
close $chan
}
socket -server accept 12345
vwait forever

client:
set chan [socket 127.0.0.1 12345]
puts $chan hello;#向通道发送hello
flush $chan
puts "server says [gets $chan]"
close $chan

运行结果:
-servr
127.0.0.1:3148 says hello
-client
server says goodbye

 

例子二:显示服务端时间
格式:clock seconds
功能:返回从计算机纪元开始的秒数,不同操作系统开始时间可能不同,所以这个值通常用来作为命
令 clock format 的输入
clock format [clock seconds] 返回当前时间
clock format [clock seconds] -format %H:%M:%S 以“时:分:秒”形式返回当前时间
-----------------------------------------------------------------------------------------
server:
proc Server {channel clientaddr clientport} {
puts "Connetion from $clientaddr $clientport registered"
puts $channel [clock format [clock seconds]]
close $channel
}
socket -server Server 9911
vwait forever

client:
set chan [socket 127.0.0.1 9911]
gets $chan line;#读取通道中的信息
close $chan
puts "The time on $chan is $line"

运行结果:
-server
Connetion from 127.0.0.1 3163 registered
-client
The time on sock268 is Tue Dec 15 09:07:00 +0800 2009

20、子进程调用--open&exec
1.格式:open |progName ?access?

功能:为管道返回一个文件描述符。如果proName用括号括起来,可以包含参数。

2.格式:exec ?switches? arg1? arg2?...?argN?

功能:执行子进程。

*switches :-keepnewline 管道输出的每行后面加个新行。

--:标志开关结束,哪怕后面跟着有-开头的字符串,都作为参数处理

*argN 参数可以是可执行程序;作为子程序运行的命令行;输出重定向。

输出重定向包括:
序号 重定向 描述
1 | 将标准输出重定向到标准输入中
2 <fileName 管道中的第一个程序将读入文件内容
3 <@fileID 管道中第一个程序将读入文件描述符中的内容,该文件描述符是使用open.."r" 的返回
4 <<value 管道中第一个程序读到这个值
5 >fileName 管道中最后一个程序的输出内容覆盖写入文件
6 >>filename 管道中最后一个程序的输出内容写入文件,添加到文件的末尾。
7 2>filename 管道中所有程序的标准错误输出到文件,覆盖文件原有内容
8 2>>filename 管道中所有程序的标准错误输出到文件,添加到文件末尾
9 >@file 管道中程序输出内容写到文件描述符,该文件描述符是用open .. W打开

代码示例如下:
set io [open "|C:/Windows/system/teacup.exe" r+];#open调用外部程序
set invert [exec C:/Windows/system/teacup.exe];#exec调用外部程序

因为管道的例子在 windows 下比较难举,现以 unix 下一段代码为例子,通过管道读出 ps(列出进程)

命令的输出:
tclfolder%tclsh
% set fid [open "|ps -ef" r+]
file5
% gets $fid line;puts "line: $line"
line: UID PID PPID C STIME TTY TIME CMD
% gets $fid line;puts "line: $line"
line: root 1 0 0 Nov 11 - 0:01 /etc/init
% gets $fid line;puts "line: $line"
line: root 86118 1 0 Nov 11 - 5:00 /usr/sbin/syncd 60
% gets $fid line;puts "line: $line"
line: tclll 90248 893094 0 11:54:55 pts/14 0:00 -csh
% gets $fid line;puts "line: $line"
line: root 110730 1 0 Nov 11 - 0:00 /usr/sbin/uprintfd
% gets $fid line;puts "line: $line"
line: root 118848 1 0 Nov 11 - 0:00 /usr/ccs/bin/shlap64
% exit
tclfolder%

 


注意事项:
1、
set a "hello"
puts "$a"
但不能写成
puts {$a}
这样的话$a不会做变量替换。
基本的替换规则:""号中的变量、命令、\替换;{}中的内容不替换。
这里的""和{}都是最外层的,如果是在里面,不影响外层是否替换的规则。

2、给参数传空值:用引号
proc cal {{a 1} {b 2} {c 3}} {
puts "!$a!$b!$c!"
}
cal 1 "" 3;#输出!1!!3!

3、给参数传"[{"a":1,"b":2},{"a":2,"b":4}]"形式的数据,方括号需要进行转义或者用{}
set data '\[{"a":1,"b":2},{"a":2,"b":4}\]'
puts $data

4、多加空格引起报错
set data '\[{"a":1,"b":2},{"a":2,"b":4} \]';#右花括号和斜杠之间有一个空格,会引起报错
puts $data
报错提示如下
wrong # args: should be "set varName ?newValue?"

5、单引号与双引号的区别是?

上一篇:[转]DesignWare是什么


下一篇:腾讯广告联盟 Android SDK(广点通)demo的使用方式