本文参考资料为《TCL语言教程》,感谢作者的分享,这里仅仅作为简单常用语法的入门,若有需要后期对本文进行添加补充。

前言(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脚本的用处目前还不是很明确,后期学习到了进行补充添加,此处作为EDA课程中的一部分进行学习)

附录(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 12*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…?

这个命令生成一个listlist的元素就是所有的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 a {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 等以及过程,source的介绍总结
TCL脚本语言的学习(二)更为详细!!

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"
}

循环命令: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

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
}

例子:

% foreach i {a b c} j {d e f g} {
puts $i
puts $j
}
a
d
b
e
c
f

g

这时总共有三次循环,x的值为”b a d c f e”。

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 {} {}”。
例子: