前言(TCL综述)
TCL(Tool Command Language)是一种解释执行的脚本语言(Scripting Language)。 它提供了 通用的编程能力:支持变量、过程和控制结构;同时 TCL还拥有一个功能强大的固有的核心命令集。
由于TCL的解释器是用一个C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作一个C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,可以很容易就在C\C++应用程序中嵌入TCL,而且每个应用程序都可以根据自己的需要对TCL语言进行扩展。我们可以针对某一特定应用领域对TCL语言的核心命令集进行扩展,加入适合于自己的应用领域的扩展命令,如果需要,甚至可以加入新的控制结构,TCL解释器将把扩展命令和扩展控制结构与固有命令和固有控制结构同等看待。扩展后的TCL语言将可以继承TCL 核心部分的所有功能,包括核心命令、控制结构、数据类型、对过程的支持等。根据需要,我们甚至可以屏蔽掉TCL的某些固有命令和固有控制结构。通过对TCL的扩展、继承或屏蔽,用户用不着象平时定义一种计算机语言那样对词法、语法、语义、语用等各方面加以定义,就可以方便的为自己的应用领域提供一种功能完备的脚本语言。
TCL良好的可扩展性使得它能很好地适应产品测试的需要,测试任务常常会由于设计和需求的改变而迅速改变,往往让测试人员疲于应付。利用TCL的可扩展性,测试人员就可以迅速继承多种新技术,并针对产品新特点迅速推出扩展TCL命令集,以用于产品的测试中,可以较容易跟上设计需求的变化。
另外,因为TCL是一种比C\C++ 语言有着更高抽象层次的语言,使用TCL可以在一种更高的层次上编写程序,它屏蔽掉了编写C\C++程序时必须涉及到的一些较为烦琐的细节,可以大大地提高开发测试例的速度。而且, 使用TCL语言写的测试例脚本,即使作了修改,也用不着重新编译就可以调用TCL解释器直接执行。可以省却不少时间。TCL 目前已成为自动测试中事实上的标准。
附录(Tcl的安装)
直接打开终端(terminal),输入 sudo apt install tcl即可进行安装,这里的截图是笔者安装成功后的实例。
之后输入tclsh即可
语法
脚本,命令和单词符号
一个TCL脚本可以包含一个或多个命令。命令之间必须用换行符或分号隔开,下面的两个脚本都是合法的:
set a 1
set b 2
或使用分号隔开
set a 1;set b 2
TCL解释器对一个命令的求值过程分为两部分:分析和执行。在分析阶段,TCL 解释器运用规则把命令分成一个个独立的单词,同时进行必要的置换(substitution); 在执行阶段,TCL 解释器会把第一个单词当作命令名,并查看这个命令是否有定义,如果有定义就激活这个命令对应的C/C++过程,并把所有的单词作为参数传递给该命令过程,让命令过程进行处理。
置换(substitution)
TCL解释器在分析命令时,把所有的命令参数都当作字符串看待,例如:
%set x 10 //定义变量x,并把x的值赋为10
10
%set y x+100 //y的值是x+100,而不是我们期望的110
x+100
上例的第二个命令中,x被看作字符串x+100的一部分,如果我们想使用x的值’10’ ,就必须告诉
TCL解释器:我们在这里期望的是变量x的值,而非字符’x’。怎么告诉TCL解释器呢,这就要用到TCL语言中提供的置换功能。置换功能分为三种.TCL提供三种形式的置换:变量置换、命令置换和反斜杠置换。每种置换都会导致一个或多个单词本身被其他的值所代替。置换可以发生在包括命令名在内的每一个单词中,而且置换可以嵌套。
变量置换variable subtitution
变量置换由一个$符号标记,变量置换会导致变量的值插入一个单词中。例如之前的一个例子
%set x 10 //定义变量x,并把x的值赋为10
10
%set y x+100 //y的值是x+100,而不是我们期望的110
x+100
%set y $x+100 //y的值是我们期望的110
110
命令置换command substitution
命令置换是由[]括起来的TCL命令及其参数,命令置换会导致某一个命令的所有或部分单词被另一个命令的结果所代替。例如:
%set y [expr $x+100]
110
这里当TCL解释器遇到字符’[‘时,它就会把随后的expr作为一个命令名,从而激活与expr对应的C/C++过程,并把expr和变量置换后得到的10+100传递给该命令过程进行处理。
反斜杠置换backslash substitution
TCL语言中的反斜杠置换类似于C语言中反斜杠的用法,主要用于在单词符号中插入诸如换行符、空格、[、$等被TCL解释器当作特殊符号对待的字符。
%set msg money\ \$3333\ \nArray\ a\[2]
//这个命令的执行结果为:
money $3333
Array a[2]
双引号和花括号
除了使用反斜杠外,TCL提供另外两种方法来使得解释器把分隔符和置换符等特殊字符当作普通字符,而不作特殊处理,这就要使用双引号和花括号({})。
TCL解释器对双引号中的各种分隔符将不作处理,但是对换行符 及$和[]两种置换符会照常处理。而在花括号中,所有特殊字符都将成为普通字符,失去其特殊意义,TCL解释器不会对其作特殊处理。
%set y "$x ddd"
100 ddd
%set y {/n$x [expr 10+100]}
/n$x [expr 10+100]
注释
TCL中的注释符是#,#和直到所在行结尾的所有字符都被TCL看作注释,TCL解释器对注释将不作任何处理。不过,要注意的是,#必须出现在TCL解释器期望命令的第一个字符出现的地方,才被当作注释。
%set a 100 # Not a comment
wrong # args: should be "set varName ?newValue?"
%set b 101 ; # this is a comment
101
变量
变量分为简单变量和数组
简单变量
一个 TCL 的简单变量包含两个部分:名字和值。名字和值都可以是任意字符串。
% set a 2
2
set a.1 4
4
% set b $a.1
2.1
在最后一个命令行,我们希望把变量a.1的值付给b,但是TCL解释器在分析时只把$符号之后直到第一个不是字母、数字或下划线的字符(这里是’.’)之间的单词符号(这里是’a’)当作要被置换的变量的名字,所以TCL解释器把a置换成2,然后把字符串“2.1”付给变量b。这显然与我们的初衷不同。
当然,如果变量名中有不是字母、数字或下划线的字符,又要用置换,可以用花括号把变量名括起来。例如:
%set b ${a.1}
4
数组
数组是一些元素的集合。TCL的数组和普通计算机语言中的数组有很大的区别。在TCL中,不能单独声明一个数组,数组只能和数组元素一起声明。数组中,数组元素的名字包含两部分:数组名和数组中元素的名字,TCL中数组元素的名字(下标〕可以为任何字符串。 例如:
set day(monday) 1
set day(tuesday) 2
set a monday
set day(monday) 1
set b $day(monday) //b 的值为 1 ,即 day(monday) 的值。
set c $day($a) //c 的值为 1 ,即 day(monday) 的值。
其他命令
unset
% unset a b day(monday)
上面的语句中删除了变量a、b和数组元素day(monday),但是数组day并没有删除,其他元素还存在,要删除整个数组,只需给出数组的名字。
append和incr
这两个命令提供了改变变量的值的简单手段。
append命令把文本加到一个变量的后面,例如:
% set txt hello
hello
% append txt "! How are you"
hello! How are you
incr命令把一个变量值加上一个整数。incr要求变量原来的值和新加的值都必须是整数。
expr
可以进行基本的数学函数计算
%expr 1 + 2*3
7
List
list这个概念在TCL中是用来表示集合的。TCL中list是由一堆元素组成的有序集合,list可以嵌套定
义,list每个元素可以是任意字符串,也可以是list。下面都是TCL中的合法的list:
{} //空list
{a b c d}
{a {b c} d} //list可以嵌套
list是TCL中比较重要的一种数据结构,对于编写复杂的脚本有很大的帮助
list
语法: list ? value value…?
这个命令生成一个list,list的元素就是所有的value。例:
% list 1 2 {3 4}
1 2 {3 4}
使用置换将其相结合
% set a {1 2 3 4 {1 2}}
1 2 3 4 {1 2}
% puts $a
1 2 3 4 {1 2}
concat
语法:concat list ?list…?
这个命令把多个list合成一个list,每个list变成新list的一个元素。
% set a {1 2 3}
1 2 3
% set b {4 5 6}
4 5 6
% concat $a $b
1 2 3 4 5 6
lindex
语法:lindex list index
返回list的第index个(0-based)元素。例:
% lindex {1 2 {3 4}} 2
3 4
llength
语法:llength list
返回list的元素个数。例
% llength {1 2 {3 4}}
3
% set a {1 2 3}
1 2 3
% llength $a
3
linsert
语法:linsert list index value ?value…?
返回一个新串,新串是把所有的value参数值插入list的第index个(0-based)元素之前得到。例:
% linsert {1 2 {3 4}} 1 7 8 {9 10}
1 7 8 {9 10} 2 {3 4}
% linsert {1 2 {3 4}} 1 {1 2 3 {4 5}}
1 {1 2 3 {4 5}} 2 {3 4}
% set a {1 2 3}
1 2 3
% linsert $a 1 {2 3 4}
1 {2 3 4} 2 3
lreplace
语法:lreplace list first last ?value value …?
返回一个新串,新串是把list的第firs (0-based)t到第last 个(0-based)元素用所有的value参数替换得到的。如果没有value参数,就表示删除第first到第last个元素。例:
% lreplace {1 7 8 {9 10} 2 {3 4}} 3 3
1 7 8 2 {3 4}
% lreplace {1 7 8 2 {3 4}} 4 4 4 5 6
1 7 8 2 4 5 6
% set a {1 2 3}
1 2 3
% lreplace $a 1 2 4 5 6 7
1 4 5 6 7
% lreplace $a 1 end
1
lrange
语法:lrange list first last
返回list的第first (0-based)到第last (0-based)元素组成的串,如果last的值是end。就是从第first个直到串的最后。
例:
% lrange {1 7 8 2 4 5 6} 3 end
2 4 5 6
% set a {1 2 3}
1 2 3
% lrange $a 0 end
1 2 3
lappend
语法:lappend varname value ?value…?
把每个value的值作为一个元素附加到变量varname后面,并返回变量的新值,如果varname不存在,就生成这个变量。例:
% set a {1 2 3}
1 2 3
% lappend a 4 5 6
1 2 3 4 5 6
lsearch
语法:lsearch ?-exact? ?-glob? ?-regexp? list pattern
返回list中第一个匹配模式pattern的元素的索引,如果找不到匹配就返回-1。-exact、-glob、 -regexp是三种模式匹配的技术。-exact表示精确匹配;-glob的匹配方式和string match命令的匹配方式相同;-regexp表示正规表达式匹配。缺省时使用-glob匹配。例:
% set a { how are you }
how are you
% lsearch $a y*
2
% lsearch $a y?
-1
-all 返回一个列表,返回的列表中的数值就是字符在列表中的位置
默认全局匹配,返回第一个字符在列表中的位置,其位缺省状态
% lsearch {a b c d e} c
2
% lsearch -all {a b c a b c} c
2 5
% lsearch {a b c d c} c
2
匹配不到返回-1
% lsearch {a b c d e} g
-1
控制流
主要是对于所有的控制流,包括 if、while、for、foreach、switch、break、continue 等以及过程,
if
语法: if test1 body1 ?elseif test2 body2 elseif…. ? ?else bodyn?
TCL先把test1当作一个表达式求值,如果值非0,则把body1当作一个脚本执行并返回所得值,否则把test2当作一个表达式求值,如果值非0,则把body2当作一个脚本执行并返回所得值……。例如:
if { $x>0 } {
.....
}elseif{ $x==1 } {
.....
}elseif { $x==2 } {
....
}else{
.....
}
if { $x<0 } {
puts "x is smaller than zero"
} elseif {$x==1} {
puts "x is equal 1"
} elseif {$x==2} {
puts "x is equal 2"
} else {
puts "x is other"
}
这里需要注意的是,
if 和{之间应该有一个空格,否则TCL解释器会把’if{‘作为一个整体当作一个命令名,从而导致错误。
‘{‘一定要写在上一行,因为如果不这样,TCL 解释器会认为if命令在换行符处已结
束,下一行会被当成新的命令,从而导致错误的结果需要将}{ 分开写,
否则会报错extra characters after close-brace
循环命令:while 、for 、 foreach
while
语法为: while test body
参数test是一个表达式,body是一个脚本,如果表达式的值非0,就运行脚本,直到表达式为0才停止循环,此时while命令中断并返回一个空字符串。
例如:假设变量 a 是一个链表,下面的脚本把a 的值复制到b:
% #首先生成一个集合
% set a {1 2 3 4}
1 2 3 4
% set b " "
% #计算生成集合的长度(从0开始这里需要减去1例如:0-3一共有四个数)
% set i [expr [llength $a] -1]
3
#接下来进行判断,将集合a中的元素全部按顺序写入b中
% while {$i>=0} {
#思考执行该行代码替换会有怎样的结果打印出来
#lappend b [lindex $a $i]
lappend b [lindex $a [expr [llength $a] - 1 - $i]]
incr i -1
}
#打印观察结果
% puts $b
1 2 3 4
对代码进行分析
set 变量a为一个list,b为一个空list
然后计算列表里有几个元素,将其减一后的值赋值给i,这里减一的目的是从零开始计数会多一个
开始进行循环,首先i的值是4大于0,表达式为真,开始执行脚本。
脚本为将数组a的第i个位置的元素添加到b list 里,然后给i减一同时进行下一次判断即可。
最后输出b的值
for
语法为: for init test reinit body
参数init是一个初始化脚本,第二个参数test是一个表达式,用来决定循环什么时候中断,第三个参数reinit是一个重新初始化的脚本,第四个参数body也是脚本,代表循环体。下例与上例作用相同:(注意这里复制打印顺序的不同)
% set a {1 2 3 4}
1 2 3 4
% set b " "
% for {set i [expr [llength $a] -1]} {$i>=0} {incr i -1} {
lappend b [lindex $a $i] }
% puts $b
4 3 2 1
例
% for {set i 0} {$i<4} {incr i} {
puts "I is: $i "
}
I is: 0
I is: 1
I is: 2
I is: 3
foreach
这个命令有两种语法形式
1, foreach varName list body
第一个参数varName是一个变量,第二个参数list 是一个表(有序集合),第三个参数body是循环体。每次取得链表的一个元素,都会执行循环体一次。 下例与上例作用相同:
% set a {1 2 3 4}
1 2 3 4
% set b " "
% foreach i $a {
set b [linsert $b 0 $i]
}
% puts $b
4 3 2 1
% foreach var {a b c d e f} {
puts $var
}
a
b
c
d
e
f
2, foreach varlist1 list1 ?varlist2 list2 ...? Body
这种形式包含了第一种形式。第一个参数varlist1是一个循环变量列表,第二个参数是一个列表list1,varlist1中的变量会分别取list1中的值。body参数是循环体。 ?varlist2 list2 …?表示可以有多个变量列表和列表对出现。例如:
set x {}
foreach {i j} {a b c d e f} {
lappend x $j $i
}
这时总共有三次循环,x的值为”b a d c f e”。
% foreach i {a b c} j {d e f g} {
puts $i
puts $j
}
a
d
b
e
c
f
g
set x {}
foreach i {a b c} j {d e f g} {
lappend x $i $j
}
这时总共有四次循环, x的值为”a d b e c f {} g
set x {}
foreach i {a b c} {j k} {d e f g} {
lappend x $i $j $k
}
这时总共有三次循环,x的值为”a d e b f g c {} {}”。
例子:
break和continue命令
在循环体中,可以用break和continue命令中断循环。其中break命令结束整个循环过程,并从循环中跳出,continue只是结束本次循环
这里有一个特别好的例子
说明:这里首先进行给一个list,然后使用foreach循环进行写入数据当遇见break时候直接退出了循环,而continue仅仅只是跳出此次循环继续向b里写入数
% set b {}
% set a {1 2 3 4 5}
1 2 3 4 5
% foreach i $a {
if {$i == 4} break
set b [linsert $b 0 $i]
}
% puts $b
3 2 1
% set b {}
% set a {1 2 3 4 5}
1 2 3 4 5
% foreach i $a {
if {$i == 4} continue
set b [linsert $b 0 $i]
}
% puts $b
5 3 2 1
switch
和 C 语言中 switch 语句一样,TCL 中的 switch 命令也可以由 if 命令实现。只是书写起来较为烦琐。 switch 命令的语法为: switch ? options? string { pattern body ? pattern body …?}
注意这里进行的是字符匹配
set x a;
set t1 0;set t2 0;set t3 0;
switch $x {
a -
b {incr t1}
c {incr t2}
default {incr t3}
}
puts "t1=$t1,t2=$t2,t3=$t3"
x=a时执行的是t1加2
其中 a 的后面跟一个’-’表示使用和下一个模式相同的脚本。default 表示匹配任意值。一旦switch 命令 找到一个模式匹配,就执行相应的脚本,并返回脚本的值,作为 switch 命令的返回值。
source
source 命令读一个文件并把这个文件的内容作为一个脚本进行求值
以上边的switch第一段代码为例
使用VIM新建一个文件,写入文件后保存退出
vim switch1.tcl
键入wish然后输入source switch1.tcl
过程(procedure)
TCL 支持过程的定义和调用,在 TCL 中,过程可以看作是用 TCL 脚本实现的命令,效果与 TCL的固有命令相似。我们可以在任何时候使用 proc 命令定义自己的过程,TCL 中的过程类似于 C中的函数。
TCL 中过程是由 proc 命令产生的:
例如:
% proc add {x y } {expr $x+$y}
roc 命令的第一个参数是你要定义的过程的名字,第二个参数是过程的参数列表,参数之间用空格隔开,第三个参数是一个 TCL 脚本,代表过程体。 proc 生成一个新的命令,可以象固有命令一样调用:
% add 1 2
3
评论 (0)