✅ 操作成功!

tcl脚本

发布时间:2023-06-06 作者:admin 来源:文学

tcl脚本

tcl脚本

地球板块图-观看警示教育片心得体会

2023年2月20日发(作者:七星瓢虫的拼音)

TCL用法祥述

一TCL语法

1脚本、命令和单词符号

一个TCL脚本可以包含一个或多个命令。命令之间必须用换行符或分号隔开,下面的两个脚本

都是合法的:

seta1

setb2

seta1;setb2

TCL的每一个命令包含一个或几个单词,第一个单词代表命令名,另外的单词则是这个命令的

参数,单词之间必须用空格或TAB键隔开。

TCL解释器对一个命令的求值过程分为两部分:分析和执行。在分析阶段,TCL解释器运用规

则把命令分成一个个独立的单词,同时进行必要的置换(substitution);在执行阶段,TCL解

释器会把第一个单词当作命令名,并查看这个命令是否有定义,如果有定义就激活这个命令对应

的C/C++过程,并把所有的单词作为参数传递给该命令过程,让命令过程进行处理。

2置换(substitution)

注:在下面的所有章节的例子中,'%'为TCL的命令提示符,输入命令回车后,TCL会在接着的

一行输出命令执行结果。'//'后面是我自己加上的说明,不是例子的一部分。

TCL解释器在分析命令时,把所有的命令参数都当作字符串看待,例如:

%setx10//定义变量x,并把x的值赋为10

10

%setyx+100//y的值是x+100,而不是我们期望的110

x+100

上例的第二个命令中,x被看作字符串x+100的一部分,如果我们想使用x的值'10',就必

须告诉TCL解释器:我们在这里期望的是变量x的值,而非字符'x'。怎么告诉TCL解释器呢,

这就要用到TCL语言中提供的置换功能。

TCL提供三种形式的置换:变量置换、命令置换和反斜杠置换。每种置换都会导致一个或多个

单词本身被其他的值所代替。置换可以发生在包括命令名在内的每一个单词中,而且置换可以嵌

套。

1)变量置换(variablesubtitution)

变量置换由一个$符号标记,变量置换会导致变量的值插入一个单词中。例如:

%sety$x+100//y的值是10+100,这里x被置换成它的值10

10+100

这时,y的值还不是我们想要的值110,而是10+100,因为TCL解释器把10+100看成是

一个字符串而不是表达式,y要想得到值110,还必须用命令置换,使得TCL会把10+100看

成一个表达式并求值。

2)命令置换(commandsubstitution)

命令置换是由[]括起来的TCL命令及其参数,命令置换会导致某一个命令的所有或部分单词被

另一个命令的结果所代替。例如:

%sety[expr$x+100]

110

y的值是110,这里当TCL解释器遇到字符'['时,它就会把随后的expr作为一个命令名,从

而激活与expr对应的C/C++过程,并把'expr'和变量置换后得到的'10+110'传递给该命令过

程进行处理。

如果在上例中我们去掉[],那么TCL会报错。因为在正常情况下,TCL解释器只把命令行中的

第一个单词作为看作命令,其他的单词都作为普通字符串处理,看作是命令的参数。

注意,[]中必须是一个合法的TCL脚本,长度不限。[]中脚本的值为最后一个命令的返回值,

例如:

%sety[expr$x+100;setb300]//y的值为300,因为setb300的返回值为300

300

有了命令置换,实际上就表示命令之间是可以嵌套的,即一个命令的结果可以作为别的命令的参

数。

3)反斜杠置换(backslashsubstitution)

TCL语言中的反斜杠置换类似于C语言中反斜杠的用法,主要用于在单词符号中插入诸如换行

符、空格、[、$等被TCL解释器当作特殊符号对待的字符。例如:

setmsgmultiplespace//msg的值为multiplespace。

如果没有''的话,TCL会报错,因为解释器会把这里最后两个单词之间的空格认为是分隔符,

于是发现set命令有多于两个参数,从而报错。加入了''后,空格不被当作分隔符,'multiple

space'被认为是一个单词(word)。又例如:

%setmsgmoney$3333nArraya[2]

//这个命令的执行结果为:money$3333

Arraya[2]

这里的$不再被当作变量置换符。

TCL支持以下的反斜杠置换:

BackslashSequenceReplacedBy

aAudiblealert(0x7)

bBackspace(0x8)

fFormfeed(0xc)

nNewline(0xa)

rCarriagereturn(0xd)

tTab(0x9)

vVerticaltab(0xb)

dddOctalvaluegivenbyddd

(one,two,orthreed's)

xhhHexvaluegivenbyhh

(anynumberofh's)

newlinespaceAsinglespacecharacter.

例如:

%setax48//对应xhh

H//十六进制的48正好是72,对应H

%seta110//对应ddd

H//八进制的110正好是72,对应H

%seta[expr/对应newlinespace,一个命令可以用newline转到下一行继续

2+3]

5

4)双引号和花括号

除了使用反斜杠外,TCL提供另外两种方法来使得解释器把分隔符和置换符等特殊字符当作普

通字符,而不作特殊处理,这就要使用双引号和花括号({})。

TCL解释器对双引号中的各种分隔符将不作处理,但是对换行符及$和[]两种置换符会照常处

理。例如:

%setx100

100

%sety"$xddd"

100ddd

而在花括号中,所有特殊字符都将成为普通字符,失去其特殊意义,TCL解释器不会对其作特

殊处理。

%sety{/n$x[expr10+100]}

/n$x[expr10+100]

3注释

TCL中的注释符是'#','#'和直到所在行结尾的所有字符都被TCL看作注释,TCL解释器对注

释将不作任何处理。不过,要注意的是,'#'必须出现在TCL解释器期望命令的第一个字符出现

的地方,才被当作注释。

例如:

%#Thisisacomment

%seta100#Notacomment

wrong#args:shouldbe"setvarName?newValue?"

%setb101;#thisisacomment

101

第二行中'#'就不被当作注释符,因为它出现在命令的中间,TCL解释器把它和后面的字符当作

命令的参数处理,从而导致错误。而第四行的'#'就被作为注释,因为前一个命令已经用一个分

号结束,TCL解释器期望下一个命令接着出现。现在在这个位置出现'#',随后的字符就被当作

注释了。

二变量

1简单变量

一个TCL的简单变量包含两个部分:名字和值。名字和值都可以是任意字符串。例如一个名为

“13237&*:hdgg"的变量在TCL中都是合法的。不过为了更好的使用置换(substitution),

变量名最好按CC++语言中标识符的命名规则命名。TCL解释器在分析一个变量置换时,只

把从$符号往后直到第一个不是字母、数字或下划线的字符之间的单词符号作为要被置换的变量

的名字。例如:

%seta2

2

seta.14

4

%setb$a.1

2.1

在最后一个命令行,我们希望把变量a.1的值付给b,但是TCL解释器在分析时只把$符号之后

直到第一个不是字母、数字或下划线的字符(这里是'.')之间的单词符号(这里是'a')当作要被置换

的变量的名字,所以TCL解释器把a置换成2,然后把字符串“2.1”付给变量b。这显然与我们

的初衷不同。

当然,如果变量名中有不是字母、数字或下划线的字符,又要用置换,可以用花括号把变量名括

起来。例如:

%setb${a.1}

4

TCL中的set命令能生成一个变量、也能读取或改变一个变量的值。例如:

%seta{kdfjkjdf}

kdfjkjdf

如果变量a还没有定义,这个命令将生成变量a,并将其值置为kdfjkjdf,若a已定义,就简

单的把a的值置为kdfjkjdf。

%seta

kdfjkjdf

这个只有一个参数的set命令读取a的当前值kdfjkjdf。

2数组

数组是一些元素的集合。TCL的数组和普通计算机语言中的数组有很大的区别。在TCL中,不

能单独声明一个数组,数组只能和数组元素一起声明。数组中,数组元素的名字包含两部分:数

组名和数组中元素的名字,TCL中数组元素的名字(下标〕可以为任何字符串。例如:

setday(monday)1

setday(tuesday)2

第一个命令生成一个名为day的数组,同时在数组中生成一个名为monday的数组元素,并把

值置为1,第二个命令生成一个名为tuesday的数组元素,并把值置为2。

简单变量的置换已经在前一节讨论过,这里讲一下数组元素的置换。除了有括号之外,数组元素

的置换和简单变量类似。例:

setamonday

setday(monday)1

setb$day(monday)//b的值为1,即day(monday)的值。

setc$day($a)//c的值为1,即day(monday)的值。

TCL不能支持复杂的数据类型,这是一个很大的缺憾,也是TCL受指责很多的方面。但是TCL

的一个扩展ITCL填补了这个缺憾。

3相关命令

set

这个命令在3.1已有详细介绍。

■unset

这个命令从解释器中删除变量,它后面可以有任意多个参数,每个参数是一个变量名,可以是简

单变量,也可以是数组或数组元素。例如:

%unsetabday(monday)

上面的语句中删除了变量a、b和数组元素day(monday),但是数组day并没有删除,其他

元素还存在,要删除整个数组,只需给出数组的名字。例如:

%puts$day(monday)

can'tread"day(monday)":nosuchelementinarray

%puts$day(tuesday)

2

%unsetday

%puts$day(tuesday)

can'tread"day(tuesday)":nosuchvariable

■append和incr

这两个命令提供了改变变量的值的简单手段。

append命令把文本加到一个变量的后面,例如:

%settxthello

hello

%appendtxt"!Howareyou"

hello!Howareyou

incr命令把一个变量值加上一个整数。incr要求变量原来的值和新加的值都必须是整数。

%setba

a

%incrb

expectedintegerbutgot"a"

%setb2

2

%incrb3

5

三表达式

1操作数

TCL表达式的操作数通常是整数或实数。整数一般是十进制的,但如果整数的第一个字符是

0(zero),那么TCL将把这个整数看作八进制的,如果前两个字符是0x则这个整数被看作是十

六进制的。TCL的实数的写法与ANSIC中完全一样。如:

2.1

7.9e+12

6e4

3.

2运算符和优先级

下面的表格中列出了TCL中用到的运算符,它们的语法形式和用法跟ANSIC中很相似。这里

就不一一介绍。下表中的运算符是按优先级从高到低往下排列的。同一格中的运算符优先级相同。

语法形式结果操作数类型

-a

!a

~a

负a

非a

int,float

int,float

int

a*b

a/b

a%b

取模

int,float

int,float

int

a+b

a-b

int,float

int,float

a<

a>>b

左移位

右移位

int

int

a

a>b

a<=b

a>=b

小于

大于

小于等于

大于等于

int,float,string

int,float,string

int,float,string

int,float,string

a==b

a!=b

等于

不等于

int,float,string

int,float,string

a&b

位操作与

int

a^b

位操作异或

int

a|b

位操作或

int

a&&b

逻辑与

int,float

a||b

逻辑或

int,float

a?b:c

选择运算

a:int,float

3数学函数

TCL支持常用的数学函数,表达式中数学函数的写法类似于CC++语言的写法,数学函数的参

数可以是任意表达式,多个参数之间用逗号隔开。例如:

%setx2

2

%expr2*sin($x<3)

1.68294196962

其中expr是TCL的一个命令,语法为:exprarg?arg...?

两个?之间的参数表示可省,后面介绍命令时对于可省参数都使用这种表示形式。expr可以

有一个或多个参数,它把所有的参数组合到一起,作为一个表达式,然后求值:

%expr1+2*3

7

%expr1+2*3

7

需要注意的一点是,数学函数并不是命令,只在表达式中出现才有意义。

TCL中支持的数学函数如下

abs(x)Absolutevalueofx.

acos(x)Arccosineofx,intherange0top.

asin(x)Arcsineofx,intherange-p/2top/2.

atan(x)Arctangentofx,intherange-p/2top/2.

atan2(x,y)Arctangentofx/y,intherange-p/2top/2.

ceil(x)Smallestintegernotlessthanx.

cos(x)Cosineofx(xinradians).

cosh(x)Hyperboliccosineofx.

double(i)Realvalueequaltointegeri.

exp(x)eraisedtothepowerx.

floor(x)Largestintegernotgreaterthanx.

fmod(x,y)Floating-pointremainderofxdividedbyy.

hypot(x,y)Squarerootof(x2+y2).

int(x)Integervalueproducedbytruncatingx.

log(x)Naturallogarithmofx.

log10(x)Base10logarithmofx.

pow(x,y)xraisedtothepowery.

round(x)Integervalueproducedbyroundingx.

sin(x)Sineofx(xinradians).

sinh(x)Hyperbolicsineofx.

sqrt(x)Squarerootofx.

tan(x)Tangentofx(xinradians).

tanh(x)Hyperbolictangentofx.

TCL中有很多命令都以表达式作为参数。最典型的是expr命令,另外if、while、for等循环控

制命令的循环控制中也都使用表达式作为参数。

四list

1List命令

list这个概念在TCL中是用来表示集合的。TCL中list是由一堆元素组成的有序集合,list可以

嵌套定义,list每个元素可以是任意字符串,也可以是list。下面都是TCL中的合法的list:

{}//空list

{abcd}

{a{bc}d}//list可以嵌套

list是TCL中比较重要的一种数据结构,对于编写复杂的脚本有很大的帮助,TCL提供了很多

基本命令对list进行操作,下面一一介绍:

语法:list?valuevalue...?

这个命令生成一个list,list的元素就是所有的value。例:

%list12{34}

12{34}

2concat命令

语法:concatlist?list...?

这个命令把多个list合成一个list,每个list变成新list的一个元素。

3lindex命令

语法:lindexlistindex

返回list的第index个(0-based)元素。例:

%lindex{12{34}}2

34

4llength命令

语法:llengthlist

返回list的元素个数。例

%llength{12{34}}

3

5linsert命令

语法:linsertlistindexvalue?value...?

返回一个新串,新串是把所有的value参数值插入list的第index个(0-based)元素之前得到。

例:

%linsert{12{34}}178{910}

178{910}2{34}

6lreplace命令

语法:lreplacelistfirstlast?valuevalue...?

返回一个新串,新串是把list的第firs(0-based)t到第last个(0-based)元素用所有的value

参数替换得到的。如果没有value参数,就表示删除第first到第last个元素。例:

%lreplace{178{910}2{34}}33

1782{34}

%lreplace{1782{34}}44456

1782456

7lrange命令

语法:lrangelistfirstlast

返回list的第first(0-based)到第last(0-based)元素组成的串,如果last的值是end。就是

从第first个直到串的最后。

例:

%lrange{1782456}3end

2456

8lappend命令

语法:lappendvarnamevalue?value...?

把每个value的值作为一个元素附加到变量varname后面,并返回变量的新值,如果varname

不存在,就生成这个变量。例:

%lappenda123

123

%seta

123

9lsearch命令

语法:lsearch?-exact??-glob??-regexp?listpattern

返回list中第一个匹配模式pattern的元素的索引,如果找不到匹配就返回-1。-exact、-glob、

-regexp是三种模式匹配的技术。-exact表示精确匹配;-glob的匹配方式和stringmatch

命令的匹配方式相同,将在后面第八节介绍string命令时介绍;-regexp表示正规表达式匹配,

将在第八节介绍regexp命令时介绍。缺省时使用-glob匹配。例:

%seta{howareyou}

howareyou

%lsearch$ay*

2

%lsearch$ay?

-1

10lsort命令

语法:lsort?options?list

这个命令返回把list排序后的串。options可以是如下值:

-ascii按ASCII字符的顺序排序比较.这是缺省情况。

-dictionary按字典排序,与-ascii不同的地方是:

(1)不考虑大小写

(2)如果元素中有数字的话,数字被当作整数来排序.

因此:bigBoy排在bigbang和bigboy之间,x10y排在x9y和x11y之间.

-integer把list的元素转换成整数,按整数排序.

-real把list的元素转换成浮点数,按浮点数排序.

-increasing升序(按ASCII字符比较)

-decreasing降序(按ASCII字符比较)

-commandcommandTCL自动利用command命令把每两个元素一一比较,然后给出排序

结果。

11split命令

语法:splitstring?splitChars?

把字符串string按分隔符splitChars分成一个个单词,返回由这些单词组成的串。如果

splitChars

是一个空字符{},string被按字符分开。如果splitChars没有给出,以空格为分隔符。例:

%split"".

howareyou

%split"howareyou"

howareyou

%split"howareyou"{}

how{}are{}you

12join命令

语法:joinlist?joinString?

join命令是命令的逆。这个命令把list的所有元素合并到一个字符串中,中间以joinString分

开。缺省的joinString是空格。例:

%join{how{}are{}you}{}

howareyou

%join{howareyou}.

五控制流

1if命令

TCL中的控制流和C语言类似,包括if、while、for、foreach、switch、break、continue

等命令。

语法:iftest1body1?elseiftest2body2elseif....??elsebodyn?

TCL先把test1当作一个表达式求值,如果值非0,则把body1当作一个脚本执行并返回所得

值,否则把test2当作一个表达式求值,如果值非0,则把body2当作一个脚本执行并返回所

得值……。例如:

if{$x>0}{

.....

}elseif{$x==1}{

.....

}elseif{$x==2}{

....

}else{

.....

}

注意,上例中'{'一定要写在上一行,因为如果不这样,TCL解释器会认为if命令在换行符处已

结束,下一行会被当成新的命令,从而导致错误的结果。在下面的循环命令的书写中也要注意这

个问题。书写中还要注意的一个问题是if和{之间应该有一个空格,否则TCL解释器会把'if{'

作为一个整体当作一个命令名,从而导致错误。

2循环命令

循环命令包括while、for、foreach等。

■while命令

语法为:whiletestbody

参数test是一个表达式,body是一个脚本,如果表达式的值非0,就运行脚本,直到表达式为

0才停止循环,此时while命令中断并返回一个空字符串。

例如:

假设变量a是一个链表,下面的脚本把a的值复制到b:

setb""

seti[expr[llength$a]-1]

while{$i>=0}{

lappendb[lindex$a$i]

incri-1

}

■for命令

语法为:forinittestreinitbody

参数init是一个初始化脚本,第二个参数test是一个表达式,用来决定循环什么时候中断,第

三个参数reinit是一个重新初始化的脚本,第四个参数body也是脚本,代表循环体。下例与

上例作用相同:

setb""

for{seti[expr[llength$a]-1]}{$i>=0}{incri-1}{

lappendb[lindex$a$i]}

■foreach命令

这个命令有两种语法形式

1)、foreachvarNamelistbody

第一个参数varName是一个变量,第二个参数list是一个表(有序集合),第三个参数body

是循环体。每次取得链表的一个元素,都会执行循环体一次。下例与上例作用相同:

setb""

foreachi$a{

setb[linsert$b0$i]

}

2)、foreachvarlist1list1?varlist2list2...?Body

这种形式包含了第一种形式。第一个参数varlist1是一个循环变量列表,第二个参数是一个列

表list1,varlist1中的变量会分别取list1中的值。body参数是循环体。?varlist2list2...?

表示可以有多个变量列表和列表对出现。例如:

setx{}

foreach{ij}{abcdef}{

lappendx$j$i

}

这时总共有三次循环,x的值为"badcfe"。

setx{}

foreachi{abc}j{defg}{

lappendx$i$j

}

这时总共有四次循环,x的值为"adbecf{}g"。

setx{}

foreachi{abc}{jk}{defg}{

lappendx$i$j$k

}

这时总共有三次循环,x的值为"adebfgc{}{}"。

break和continue命令

在循环体中,可以用break和continue命令中断循环。其中break命令结束整个循环过程,

并从循环中跳出,continue只是结束本次循环。

■switch命令

和C语言中switch语句一样,TCL中的switch命令也可以由if命令实现。只是书写起来较为

烦琐。switch命令的语法为:switch?options?string{patternbody?pattern

body...?}

第一个是可选参数options,表示进行匹配的方式。TCL支持三种匹配方式:-exact方式,-glob

方式,-regexp方式,缺省情况表示-glob方式。-exact方式表示的是精确匹配,-glob方式的

匹配方式和stringmatch命令的匹配方式相同(第八节介绍),-regexp方式是正规表达式的

匹配方式(第八节介绍)。第二个参数string是要被用来作测试的值,第三个参数是括起来的一

个或多个元素对,例:

switch$x{

a-

b{incrt1}

c{incrt2}

default{incrt3}

}

其中a的后面跟一个'-'表示使用和下一个模式相同的脚本。default表示匹配任意值。一旦

switch命令找到一个模式匹配,就执行相应的脚本,并返回脚本的值,作为switch命令的返

回值。

3eval命令

eval命令是一个用来构造和执行TCL脚本的命令,其语法为:

evalarg?arg...?

它可以接收一个或多个参数,然后把所有的参数以空格隔开组合到一起成为一个脚本,然后对这

个脚本进行求值。例如:

%evalseta2;setb4

4

4source命令

source命令读一个文件并把这个文件的内容作为一个脚本进行求值。例如:

sourcee:/tcl&c/

注意路径的描述应该和UNIX相同,使用'/'而不是''。

六过程(procedure)

1过程定义和返回值

TCL支持过程的定义和调用,在TCL中,过程可以看作是用TCL脚本实现的命令,效果与TCL

的固有命令相似。我们可以在任何时候使用proc命令定义自己的过程,TCL中的过程类似于C

中的函数。

TCL中过程是由proc命令产生的:

例如:

%procadd{xy}{expr$x+$y}

proc命令的第一个参数是你要定义的过程的名字,第二个参数是过程的参数列表,参数之间用

空格隔开,第三个参数是一个TCL脚本,代表过程体。proc生成一个新的命令,可以象固有

命令一样调用:

%add12

3

在定义过程时,你可以利用return命令在任何地方返回你想要的值。return命令迅速中断过

程,并把它的参数作为过程的结果。例如:

%procabs{x}{

if{$x>=0}{return$x}

return[expr-$x]

}

过程的返回值是过程体中最后执行的那条命令的返回值。

2局部变量和全局变量

对于在过程中定义的变量,因为它们只能在过程中被访问,并且当过程退出时会被自动删除,所

以称为局部变量;在所有过程之外定义的变量我们称之为全局变量。TCL中,局部变量和全局

变量可以同名,两者的作用域的交集为空:局部变量的作用域是它所在的过程的内部;全局变量

的作用域则不包括所有过程的内部。这一点和C语言有很大的不同.

如果我们想在过程内部引用一个全局变量的值,可以使用global命令。例如:

%seta4

4

%procsample{x}{

globala

incra

return[expr$a+$x]

}

%sample3

8

%seta

5

全局变量a在过程中被访问。在过程中对a的改变会直接反映到全局上。如果去掉语句globala,

TCL会出错,因为它不认识变量a。

3缺省参数和可变个数参数

TCL还提供三种特殊的参数形式:

首先,你可以定义一个没有参数的过程,例如:

procadd{}{expr2+3}

其次,可以定义具有缺省参数值的过程,我们可以为过程的部分或全部参数提供缺省值,如果调

用过程时未提供那些参数的值,那么过程会自动使用缺省值赋给相应的参数。和CC++中具有

缺省参数值的函数一样,有缺省值的参数只能位于参数列表的后部,即在第一个具有缺省值的参

数后面的所有参数,都只能是具有缺省值的参数。

例如:

procadd{val1{val22}{val33}}{

expr$val1+$val2+$val3

}

则:

add1//值为6

add220//值为25

add456//值为15

另外,TCL的过程定义还支持可变个数的参数,如果过程的最后一个参数是args,那么就表示

这个过程支持可变个数的参数调用。调用时,位于args以前的参数象普通参数一样处理,但任

何附加的参数都需要在过程体中作特殊处理,过程的局部变量args将会被设置为一个列表,其

元素就是所有附加的变量。如果没有附加的变量,args就设置成一个空串,下面是一个例子:

procadd{val1args}{

setsum$val1

foreachi$args{

incrsum$i

}

return$sum

}

则:

add2//值为2

add23456//值为20

4引用:upvar

命令语法:upvar?level?otherVarmyVar?otherVarmyVar...?

upvar命令使得用户可以在过程中对全局变量或其他过程中的局部变量进行访问。upvar命令

的第一个参数otherVar是我们希望以引用方式访问的参数的名字,第二个参数myVar是这个

过程中的局部变量的名字,一旦使用了upvar命令把otherVar和myVar绑定,那么在过程

中对局部变量myVar的读写就相当于对这个过程的调用者中otherVar所代表的局部变量的

读写。下面是一个例子:

%proctemp{arg}{

upvar$argb

setb[expr$b+2]

}

%procmyexp{var}{

seta4

tempa

return[expr$var+$a]

}

则:

%myexp7

13

这个例子中,upvar把$arg(实际上是过程myexp中的变量a)和过程temp中的变量b绑定,

对b的读写就相当于对a的读写。

upvar命令语法中的level参数表示:调用upvar命令的过程相对于我们希望引用的变量

myVar在调用栈中相对位置。例如:

upvar2otherx

这个命令使得当前过程的调用者的调用者中的变量other,可以在当前过程中利用x访问。缺省

情况下,level的值为1,即当前过程(上例中的temp)的调用者(上例中的myexp)中的变量(上

例中myexp的a)可以在当前过程中利用局部变量(上例中temp的b)访问。

如果要访问全局变量可以这样写:

upvar#0otherx

那么,不管当前过程处于调用栈中的什么位置,都可以在当前过程中利用x访问全局变量other。

七字符串操作

1format命令

因为TCL把所有的输入都当作字符串看待,所以TCL提供了较强的字符串操作功能,TCL中与

字符串操作有关的命令有:string、format、regexp、regsub、scan等。

format命令

语法:formatformatstring?vluevalue...?

format命令类似于ANSIC中的sprintf函数和MFC中CString类提供的Format成员函数。

它按formatstring提供的格式,把各个value的值组合到formatstring中形成一个新字符串,

并返回。例如:

%setnamejohn

John

%setage20

20

%setmsg[format"%sis%dyearsold"$name$age]

johnis20yearsold

2scan命令

语法:scanstringformatvarName?varName...?

scan命令可以认为是format命令的逆,其功能类似于ANSIC中的sscanf函数。它按format

提供的格式分析string字符串,然后把结果存到变量varName中,注意除了空格和TAB键之

外,string和format中的字符和'%'必须匹配。例如:

%scan"some2634""some%d%d"ab

2

%seta

26

%setb

34

%scan"12.34.56.78""%d.%d.%d.%d"cdef

4

%puts[format"thevalueofcis%d,dis%d,eis%d,fis%d"$c$d$e$f]

thevalueofcis12,dis34,eis56,fis78

scan命令的返回值是匹配的变量个数。而且,我们发现,如果变量varName不存在的话,TCL

会自动声明该变量。

3regexp命令

语法:regexp?switchs??--?expstring?matchVar??subMatchVarsubMatchVar...?

regexp命令用于判断正规表达式exp是否全部或部分匹配字符串string,匹配返回1,否则0。

在正规表达式中,一些字符具有特殊的含义,下表一一列出,并给予了解释。

字符意义

.匹配任意单个字符

^

表示从头进行匹配

$

表示从末尾进行匹配

x

匹配字符x,这可以抑制字符x的含义

[chars]

匹配字符集合chars中给出的任意字符,如果chars中的第一个字符是^,

表示匹配任意不在chars中的字符,chars的表示方法支持a-z之类的表

示。

(regexp)

把regexp作为一个单项进行匹配

*

对*前面的项0进行次或多次匹配

+

对+前面的项进行1次或多次匹配

对?前面的项进行0次或1次匹配

regexp1|regexp2

匹配regexp1或regexp2中的一项

下面的一个例子是从《TclandTkToolKit》中摘下来的,下面进行说明:

“[^n]*n”匹配所有到换行符为止的内容。

^((0x)?[0-9a-fA-F]+|[0-9]+)$

这个正规表达式匹配任何十六进制或十进制的整数。

两个正规表达式以|分开(0x)?[0-9a-fA-F]+和[0-9]+,表示可以匹配其中的任何一个,事实

上前者匹配十六进制,后者匹配的十进制。

^表示必须从头进行匹配,从而上述正规表达式不匹配jk12之类不是以0x或数字开头的串。

$表示必须从末尾开始匹配,从而上述正规表达式不匹配12jk之类不是数字或a-fA-F结尾的

串。

下面以(0x)?[0-9a-fA-F]+进行说明,(0x)表示0x一起作为一项,?表示前一项(0x)可以出

现0次或多次,[0-9a-fA-F]表示可以是任意0到9之间的单个数字或a到f或A到F之间的

单个字母,+表示象前面那样的单个数字或字母可以重复出现一次或多次。

%regexp{^((0x)?[0-9a-fA-F]+|[0-9]+)$}ab

1

%regexp{^((0x)?[0-9a-fA-F]+|[0-9]+)$}0xabcd

1

%regexp{^((0x)?[0-9a-fA-F]+|[0-9]+)$}12345

1

%regexp{^((0x)?[0-9a-fA-F]+|[0-9]+)$}123j

0

如果regexp命令后面有参数matchVar和subMatchVar,则所有的参数被当作变量名,如果

变量不存在,就会被生成。regexp把匹配整个正规表达式的子字符串赋给第一个变量,匹配

正规表达式的最左边的子表达式的子字符串赋给第二个变量,依次类推,例如:

%regexp{([0-9]+)*([a-z]+)}"thereis100apples"totalnumword

1

%puts"$total,$num,$word"

100apples,100,apples

regexp可以设置一些开关(switchs〕,来控制匹配结果:

开关意义

-nocase

匹配时不考虑大小写

-indices

改变各个变量的值,这使各个变量的值变成了对应的匹配子串在整个字符串

中所处位置的索引。例如:

%regexp-indices{([0-9]+)*([a-z]+)}"thereis100apples"

totalnumword

1

%puts"$total,$num,$word"

920,1012,1520

正好子串“100apples”的序号是9-20,"100"的序号是10-12,"apples"的

序号是15-20

-about

返回正则表达式本身的信息,而不是对缓冲区的解析。返回的是一个list,第

一个元素是子表达式的个数,第二个元素开始存放子表达式的信息

-expanded

启用扩展的规则,将空格和注释忽略掉,相当于使用内嵌语法(?x)

-line

启用行敏感匹配。正常情况下^和$只能匹配缓冲区起始和末尾,对于缓冲区

内部新的行是不能匹配的,通过这个开关可以使缓冲区内部新的行也可以被

匹配。它相当于同时使用-linestop和-lineanchor

开关,或者使用内嵌语法(?n)

-linestop

启动行结束敏感开关。使^可以匹配缓冲区内部的新行。相当于内嵌语法(?p)

-lineanchor

改变^和$的匹配行为,使可以匹配缓冲区内部的新行。相当于内嵌语法(?w)

-all

进最大可能的匹配

-inlineCausesthecommandtoreturn,asalist,thedatathatwould

ing-inline,match

with-all,thelistwillbe

concatenatedateachiteration,suchthataflatlistisalways

hmatchiteration,thecommandwillappendthe

overallmatchdata,plusoneelementforeachsubexpressioninthe

esare:

regexp-inline--{w(w)}"inlined"

=>{inn}

regexp-all-inline--{w(w)}"inlined"

=>{innliinee}

-startindex

强制从偏移为index开始的位置进行匹配。使用这个开关之后,^将不能匹

配行起始位置,A将匹配字符串的index偏移位置。如果使用了-indices

开关,则indices表示绝对位置,index表示输入字符的相对位置。

--

表示这后面再没有开关(switchs〕了,即使后面有以'-'开头的参数也被当作

正规表达式的一部分。

【TCL正则表达式规则详细说明】

◆DESCRIPTION(描述)

'sapatternthatmatches

certainstringsanddoesn'tmatchothers.

◆DIFFERENTFLAVORSOFREs(和标准正则表达式的区别)

Regularexpressions,asdefinedbyPOSIX,comeintwoflavors:extendedREsand

eroughlythoseofthetraditionalegrep,whileBREsareroughly

plementationaddsathirdflavor,advancedREs,

basicallyEREswithsomesignificantextensions.

stlyexistforbackward

compatibilityinsomeoldprograms;REs

esofAREsthatarenotpresentinEREs

willbeindicated.

◆REGULAREXPRESSIONSYNTAX(语法)

TclregularexpressionsareimplementedusingthepackagewrittenbyHenry

Spencer,basedonthe1003.2specandsome(notquiteall)ofthePerl5extensions

(thanks,Henry!).Muchofthedescriptionofregularexpressionsbelowiscopied

verbatimfromhismanualentry.

AnAREisoneormorebranches,separatedby`|',matchinganythingthatmatches

anyofthebranches.

Abranchiszeroormoreconstraintsorquantifiedatoms,hes

amatchforthefirst,followedbyamatchforthesecond,etc;anemptybranch

matchestheemptystring.

ta

quantifier,ntifiers,andwhata

so-quantifiedatommatches,are:

字符意义

*asequenceof0ormorematchesoftheatom

+asequenceof1ormorematchesoftheatom

asequenceof0or1matchesoftheatom

{m}asequenceofexactlymmatchesoftheatom

{m,}asequenceofmormorematchesoftheatom

{m,n}asequenceofmthroughn(inclusive)matchesoftheatom;mmay

notexceedn

*?+???

{m}?{m,}?

{m,n}?

non-greedyquantifiers,whichmatchthesamepossibilities,but

preferthesmallestnumberratherthanthelargestnumberof

matches(seeMATCHING)

Theformsusing{and}bersmandnareunsigned

decimalintegerswithpermissiblevaluesfrom0to255inclusive.

Anatomisoneof:

字符意义

(re)(wherereisanyregularexpression)matchesamatchforre,withthe

matchnotedforpossiblereporting

(?:re)asprevious,butdoesnoreporting

()matchesanemptystring,notedforpossiblereporting

(?:)matchesanemptystring,withoutreporting

[chars]abracketexpression,matchinganyoneofthechars(seeBRACKET

EXPRESSIONSformoredetail)

.matchesanysinglecharacter

kwherekisanon-alphanumericcharacter)matchesthatcharacter

takenasanordinarycharacter,sabackslashcharacter

cwherecisalphanumeric(possiblyfollowedbyothercharacters),an

escape(AREsonly),seeESCAPESbelow

{whenfollowedbyacharacterotherthanadigit,matchesthe

left-bracecharacter`{';whenfollowedbyadigit,itisthebeginning

ofabound(seeabove)

xwherexisasinglecharacterwithnoothersignificance,matchesthat

character.

Aconstraintmatchesanemptystringwhenspecificconditionsaremet.A

pleconstraintsareasfollows;

somemoreconstraintsaredescribedlater,underESCAPES.

字符意义

^matchesatthebeginningofaline

$matchesattheendofaline

(?=re)positivelookahead(AREsonly),matchesatanypointwherea

substringmatchingrebegins

(?!re)negativelookahead(AREsonly),matchesatanypointwhereno

substringmatchingrebegins

Thelookaheadconstraintsmaynotcontainbackreferences(seelater),andall

parentheseswithinthemareconsiderednon-capturing.

AnREmaynotendwith`'.

◆BRACKETEXPRESSIONS(预定义表达式)

Abracketexpressionisalistofcharactersenclosedin`[]'.Itnormallymatchesany

singlecharacterfromthelist(butseebelow).Ifthelistbeginswith`^',itmatches

anysinglecharacter(butseebelow)notfromtherestofthelist.

Iftwocharactersinthelistareseparatedby`-',thisisshorthandforthefullrange

ofcharactersbetweenthosetwo(inclusive)inthecollatingsequence,e.g.[0-9]in

gesmaynotshareanendpoint,soe.g.

areverycollating-sequence-dependent,andportable

programsshouldavoidrelyingonthem.

Toincludealiteral]or-inthelist,thesimplestmethodistoencloseitin[.and.]

tomakeitacollatingelement(seebelow).Alternatively,makeitthefirstcharacter

(followingapossible`^'),or(AREsonly)precedeitwith`'.Alternatively,for`-',

makeitthelastcharacter,literal-asthe

firstendpointofarange,makeitacollatingelementor(AREsonly)precedeitwith

`'.Withtheexceptionofthese,somecombinationsusing[(seenextparagraphs),

andescapes,allotherspecialcharacterslosetheirspecialsignificancewithina

bracketexpression.

Withinabracketexpression,acollatingelement(acharacter,amulti-character

sequencethatcollatesasifitwereasinglecharacter,oracollating-sequencename

foreither)enclosedin[.and.]standsforthesequenceofcharactersofthat

uenceisasingleelementofthebracketexpression'slist.

Abracketexpressioninalocalethathasmulti-charactercollatingelementscanthus

(insidiously),abracketexpressionthatstarts

with^canmatchmulti-charactercollatingelementsevenifnoneofthemappearin

thebracketexpression!(Note:Tclcurrentlyhasnomulti-charactercollating

formationisonlyforillustration.)

Forexample,assumethecollatingsequenceincludesachmulti-charactercollating

eRE[[.ch.]]*c(zeroormorech'sfollowedbyc)matchesthefirst

fivecharactersof`chchcc'.Also,theRE[^c]bmatchesallof`chb'(because[^c]

matchesthemulti-characterch).

Withinabracketexpression,acollatingelementenclosedin[=and=]isan

equivalenceclass,standingforthesequencesofcharactersofallcollatingelements

equivalenttothatone,includingitself.(Iftherearenootherequivalentcollating

elements,thetreatmentisasiftheenclosingdelimiterswere`[.'and`.]'.)For

example,ifoand?arethemembersofanequivalenceclass,then`[[=o=]]',

`[[=?]]',and`[o'valenceclassmaynotbeanendpoint

ofarange.(Note:n't

mplesabovearejustillustrations.)

Withinabracketexpression,thenameofacharacterclassenclosedin[:and:]

standsforthelistofallcharacters(notallcollatingelements!)belongingtothat

rdcharacterclassesare:

字符意义

alphaAletter.

upperAnupper-caseletter.

lowerAlower-caseletter.

digitAdecimaldigit.

xdigitAhexadecimaldigit.

alnumAnalphanumeric(letterordigit).

printAnalphanumeric(sameasalnum).

blankAspaceortabcharacter.

spaceAcharacterproducingwhitespaceindisplayedtext.

punctApunctuationcharacter.

graphAcharacterwithavisiblerepresentation.

cntrlAcontrolcharacter.

Alocalemayprovideothers.(NotethatthecurrentTclimplementationhasonlyone

locale:theUnicodelocale.)Acharacterclassmaynotbeusedasanendpointofa

range.

Therearetwospecialcasesofbracketexpressions:thebracketexpressions[[:<:]]

and[[:>:]]areconstraints,matchingemptystringsatthebeginningandendofa

sdefinedasasequenceofwordcharactersthatisneither

haracterisanalnumcharacter

oranunderscore(_).Thesespecialbracketexpressionsaredeprecated;usersof

AREsshoulduseconstraintescapesinstead(seebelow).

◆ESCAPES(转意字符)

Escapes(AREsonly),whichbeginwithafollowedbyanalphanumericcharacter,

comeinseveralvarieties:characterentry,classshorthands,constraintescapes,

wedbyanalphanumericcharacterbutnotconstituting

,therearenoescapes:outsideabracket

expression,afollowedbyanalphanumericcharactermerelystandsforthat

characterasanordinarycharacter,andinsideabracketexpression,isanordinary

character.(ThelatteristheoneactualincompatibilitybetweenEREsandAREs.)

Character-entryescapes(AREsonly)existtomakeiteasiertospecifynon-printing

andotherwiseinconvenientcharactersinREs:

字符意义

aalert(bell)character,asinC

bbackspace,asinC

Bsynonymfortohelpreducebackslashdoublinginsome

applicationswheretherearemultiplelevelsofbackslashprocessing

cX(whereXisanycharacter)thecharacterwhoselow-order5bitsare

thesameasthoseofX,andwhoseotherbitsareallzero

ethecharacterwhosecollating-sequencenameis`ESC',orfailing

that,thecharacterwithoctalvalue033

fformfeed,asinC

nnewline,asinC

rcarriagereturn,asinC

thorizontaltab,asinC

uwxyz(wherewxyzisexactlyfourhexadecimaldigits)theUnicode

characterU+wxyzinthelocalbyteordering

Ustuvwxyz(wherestuvwxyzisexactlyeighthexadecimaldigits)reservedfora

somewhat-hypotheticalUnicodeextensionto32bits

vverticaltab,asinCareallavailable.

xhhh(wherehhhisanysequenceofhexadecimaldigits)thecharacter

whosehexadecimalvalueis0xhhh(asinglecharacternomatter

howmanyhexadecimaldigitsareused).

0thecharacterwhosevalueis0

xy(wherexyisexactlytwooctaldigits,andisnotabackreference

(seebelow))thecharacterwhoseoctalvalueis0xy

xyz(wherexyzisexactlythreeoctaldigits,andisnotabackreference

(seebelow))thecharacterwhoseoctalvalueis0xyz

Hexadecimaldigitsare`0'-`9',`a'-`f',and`A'-`F'.Octaldigitsare`0'-`7'.

mple,

135is]inASCII,,

however,thatsomeapplications(e.g.,Ccompilers)interpretsuchsequences

themselvesbeforetheregular-expressionpackagegetstoseethem,whichmay

requiredoubling(quadrupling,etc.)the`'.

Class-shorthandescapes(AREsonly)provideshorthandsforcertain

commonly-usedcharacterclasses:

缩写代表的完整表达式

d[[:digit:]]

s[[:space:]]

w[[:alnum:]_](noteunderscore)

D[^[:digit:]]

S[^[:space:]]

W[^[:alnum:]_](noteunderscore)

Withinbracketexpressions,`d',`s',and`w'losetheirouterbrackets,and`D',

`S',and`W'areillegal.(So,forexample,[a-cd]isequivalentto[a-c[:digit:]].

Also,[a-cD],whichisequivalentto[a-c^[:digit:]],isillegal.)

Aconstraintescape(AREsonly)isaconstraint,matchingtheemptystringifspecific

conditionsaremet,writtenasanescape:

字符意义

Amatchesonlyatthebeginningofthestring(seeMATCHING,below,

forhowthisdiffersfrom`^')

mmatchesonlyatthebeginningofaword

Mmatchesonlyattheendofaword

ymatchesonlyatthebeginningorendofaword

Ymatchesonlyatapointthatisnotthebeginningorendofaword

Zmatchesonlyattheendofthestring(seeMATCHING,below,forhow

thisdiffersfrom`$')

m(wheremisanonzerodigit)abackreference,seebelow

mnn(wheremisanonzerodigit,andnnissomemoredigits,andthe

decimalvaluemnnisnotgreaterthanthenumberofclosingcapturing

parenthesesseensofar)abackreference,seebelow

Awordisdefinedasinthespecificationof[[::]]aint

escapesareillegalwithinbracketexpressions.

Abackreference(AREsonly)matchesthesamestringmatchedbythe

parenthesizedsubexpressionspecifiedbythenumber,sothat(e.g.)([bc])1

matchesbborccbutnot`bc'.Thesubexpressionmustentirelyprecedetheback

ressionsarenumberedintheorderoftheirleading

-capturingparenthesesdonotdefinesubexpressions.

Thereisaninherenthistoricalambiguitybetweenoctalcharacter-entryescapesand

backreferences,whichisresolvedbyheuristics,ngzero

enon-zerodigit,notfollowedbyanother

digit,-digitsequencenotstartingwitha

zeroistakenasabackreferenceifitcomesafterasuitablesubexpression(

numberisinthelegalrangeforabackreference),andotherwiseistakenasoctal.

◆METASYNTAX(内嵌语法)

Inadditiontothemainsyntaxdescribedabove,therearesomespecialformsand

miscellaneoussyntacticfacilitiesavailable.

NormallytheflavorofREbeingusedisspecifiedbyapplication-dependentmeans.

However,ofanyflavorbeginswith

`***:',ofanyflavorbeginswith`***=',the

restoftheREistakentobealiteralstring,withallcharactersconsideredordinary

characters.

AnAREmaybeginwithembeddedoptions:asequence(?xyz)(wherexyzisoneor

morealphabeticcharacters)

supplement,andcanoverride,

availableoptionlettersare:

字符意义

brestofREisaBRE

ccase-sensitivematching(usualdefault)

erestofREisanERE

icase-insensitivematching(seeMATCHING,below)

mhistoricalsynonymforn

nnewline-sensitivematching(seeMATCHING,below)

ppartialnewline-sensitivematching(seeMATCHING,below)

qrestofREisaliteralstring,allordinarycharacters

snon-newline-sensitivematching(usualdefault)

ttightsyntax(usualdefault;seebelow)

winversepartialnewline-sensitivematching(seeMATCHING,below)

xexpandedsyntax(seebelow)

Embeddedoptionstakeeffectatthe)eavailable

onlyatthestartofanARE,andmaynotbeusedlaterwithinit.

Inadditiontotheusual(tight)REsyntax,inwhichallcharactersaresignificant,

thereisanexpandedsyntax,availableinallflavorsofREwiththe-expandedswitch,

xpandedsyntax,white-space

charactersareignoredandallcharactersbetweena#andthefollowingnewline(or

theendoftheRE)areignored,permittingparagraphingandcommentingacomplex

rethreeexceptionstothatbasicrule:

awhite-spacecharacteror`#'precededby`'isretained

whitespaceor`#'withinabracketexpressionisretained

whitespaceandcommentsareillegalwithinmulti-charactersymbolsliketheARE

`(?:'ortheBRE`('

Expanded-syntaxwhite-spacecharactersareblank,tab,newline,andanycharacter

thatbelongstothespacecharacterclass.

Finally,inanARE,outsidebracketexpressions,thesequence`(?#ttt)'(wheretttis

anytextnotcontaininga`)')isacomment,,thisisnot

allowedbetweenthecharactersofmulti-charactersymbolslike`(?:'.Such

commentsaremoreahistoricalartifactthanausefulfacility,andtheiruseis

deprecated;usetheexpandedsyntaxinstead.

Noneofthesemetasyntaxextensionsisavailableiftheapplication(oraninitial

***=director)hasspecifiedthattheuser'sinputbetreatedasaliteralstringrather

thanasanRE.

◆MATCHING(匹配)

IntheeventthatanREcouldmatchmorethanonesubstringofagivenstring,the

Ecouldmatchmorethan

onesubstringstartingatthatpoint,itschoiceisdeterminedbyitspreference:either

thelongestsubstring,ortheshortest.

Mostatoms,andallconstraints,thesizedREhasthe

samepreference(possiblynone)ifiedatomwithquantifier{m}

or{m}?hasthesamepreference(possiblynone)ified

atomwithothernormalquantifiers(including{m,n}withmequalton)prefers

ifiedatomwithothernon-greedyquantifiers(including

{m,n}?withmequalton)hhasthesame

nsisting

oftwoormorebranchesconnectedbythe|operatorpreferslongestmatch.

SubjecttotheconstraintsimposedbytherulesformatchingthewholeRE,

subexpressionsalsomatchthelongestorshortestpossiblesubstrings,basedon

theirpreferences,withsubexpressionsstartingearlierintheREtakingpriorityover

atoutersubexpressionsthustakepriorityovertheir

componentsubexpressions.

Notethatthequantifiers{1,1}and{1,1}?canbeusedtoforcelongestandshortest

preference,respectively,onasubexpressionorawholeRE.

Matchlengthsaremeasuredincharacters,ystring

mple,bb*matchesthethree

middlecharactersof`abbbc',(week|wee)(night|knights)matchesallten

charactersof`

weeknights',when(.*).*ismatchedagainstabctheparenthesizedsubexpression

matchesallthreecharacters,andwhen(a*)*ismatchedagainstbcboththewhole

REandtheparenthesizedsubexpressionmatchanemptystring.

Ifcase-independentmatchingisspecified,theeffectismuchasifallcase

alphabeticthatexistsin

multiplecasesappearsasanordinarycharacteroutsideabracketexpression,itis

effectivelytransformedintoabracketexpressioncontainingbothcases,sothatx

becomes`[xX]'.Whenitappearsinsideabracketexpression,allcasecounterparts

ofitareaddedtothebracketexpression,sothat[x]becomes[xX]and[^x]

becomes`[^xX]'.

Ifnewline-sensitivematchingisspecified,.andbracketexpressionsusing^will

nevermatchthenewlinecharacter(sothatmatcheswillnevercrossnewlines

unlesstheREexplicitlyarrangesit)and^and$willmatchtheemptystringafter

andbeforeanewlinerespectively,inadditiontomatchingatbeginningandendof

dZcontinuetomatchbeginningorendofstringonly.

Ifpartialnewline-sensitivematchingisspecified,cket

expressionsaswithnewline-sensitivematching,butnot^and`$'.

Ifinversepartialnewline-sensitivematchingisspecified,thisaffects^and$aswith

newline-sensitivematching,n'tveryuseful

butisprovidedforsymmetry.

◆LIMITSANDCOMPATIBILITY(限制和兼容性)

msintendedtobehighly

portableshouldnotemployREslongerthan256bytes,asaPOSIX-compliant

implementationcanrefusetoacceptsuchREs.

TheonlyfeatureofAREsthatisactuallyincompatiblewithPOSIXEREsisthatdoes

erAREfeatures

usesyntaxwhichisillegalorhasundefinedorunspecifiedeffectsinPOSIXEREs;

the***syntaxofdirectorslikewiseisoutsidethePOSIXsyntaxforbothBREsand

EREs.

ManyoftheAREextensionsareborrowedfromPerl,butsomehavebeenchanged

tocleanthemup,atibilitiesof

noteinclude`b',`B',thelackofspecialtreatmentforatrailingnewline,the

additionofcomplementedbracketexpressionstothethingsaffectedby

newline-sensitivematching,therestrictionsonparenthesesandbackreferencesin

lookaheadconstraints,andthelongest/shortest-match(ratherthanfirst-match)

matchingsemantics.

ThematchingrulesforREscontainingbothnormalandnon-greedyquantifiershave

changedsinceearlybeta-testversionsofthispackage.(Thenewrulesaremuch

simplerandcleaner,butdon'tworkashardatguessingtheuser'srealintentions.)

HenrySpencer'soriginal1986regexppackage,stillinwidespreaduse(e.g.,in

pre-8.1releasesofTcl),implementedanearlyversionoftoday're

fourincompatibilitiesbetweenregexp'snear-EREs(`RREs'forshort)

roughlyincreasingorderofsignificance:

InAREs,followedbyanalphanumericcharacteriseitheranescapeoranerror,

whileinRREs,ouldnot

beaproblembecausetherewasnoreasontowritesuchasequenceinRREs.

{followedbyadigitinanAREisthebeginningofabound,whileinRREs,{was

quencesshouldberare,andwilloftenresult

inanerrorbecausefollowingcharacterswillnotlooklikeavalidbound.

InAREs,remainsaspecialcharacterwithin`[]',soaliteralwithin[]mustbe

written`'.alsogivesaliteralwithin[]inRREs,butonlytrulyparanoid

programmersroutinelydoubledthebackslash.

AREsreportthelongest/shortestmatchfortheRE,ratherthanthefirstfoundina

yaffectsomeRREswhichwerewritteninthe

expectationthatthefirstmatchwouldbereported.(ThecarefulcraftingofRREsto

optimizethesearchorderforfastmatchingisobsolete(AREsexamineallpossible

matchesinparallel,andtheirperformanceislargelyinsensitivetotheircomplexity)

butcaseswherethesearchorderwasexploitedtodeliberatelyfindamatchwhich

wasnotthelongest/shortestwillneedrewriting.)

◆BASICREGULAREXPRESSIONS(基本正则表达式)

BREsdifferfromEREsinseveralrespects.`|',`+',and?areordinarycharacters

imitersforboundsare

{and`}',with{and}enthesesfor

nestedsubexpressionsare(and`)',with(and)bythemselvesordinary

characters.^isanordinarycharacterexceptatthebeginningoftheREorthe

beginningofaparenthesizedsubexpression,$isanordinarycharacterexceptat

theendoftheREortheendofaparenthesizedsubexpression,and*isanordinary

characterifitappearsatthebeginningoftheREorthebeginningofaparenthesized

subexpression(afterapossibleleading`^').Finally,single-digitbackreferences

areavailable,andaresynonymsfor[[::]]respectively;no

otherescapesareavailable.

4regsub命令

语法:regsub?switchs?expstringsubSpecvarname

regsub的第一个参数是一个整个表达式,第二个参数是一个输入字符串,这一点和regexp命

令完全一样,也是当匹配时返回1,否则返回0。不过regsub用第三个参数的值来替换字符串

string中和正规表达式匹配的部分,第四个参数被认为是一个变量,替换后的字符串存入这个

变量中。例如:

%regsubthere"Theylivetherelives"theirx

1

%puts$x

Theylivetheirlives

这里there被用their替换了。

regsub命令也有几个开关(switchs):

-nocase意义同regexp命令中。

-all没有这个开关时,regsub只替换第一个匹配,有了这个开关,regsub将把所有匹配的地

方全部替换。

--意义同regexp命令中。

5string命令

string命令的语法:stringoptionarg?arg...?

string命令具有强大的操作字符串的功能,其中的option选项多达20个。下面介绍其中常用

的部分。

■1、stringcompare?-nocase??-lengthint?string1string2

把字符串string1和string2进行比较,返回值为-1、0或1,分别对应string1小于、等于或大

于string2。如果有-length参数,那么只比较前int个字符,如果int为负数,那么这个参

数被忽略。如果有-nocase参数,那么比较时不区分大小写。

■2、stringequal?-nocase??-lengthint?string1string2

把字符串string1和string2进行比较,如果两者相同,返回值为1,否则返回0。其他参数与

8.5.1同。

■3、stringfirststring1string2?startindex?

在string2中从头查找与string1匹配的字符序列,如果找到,那么就返回匹配的第一个字母

所在的位置(0-based)。如果没有找到,那么返回-1。如果给出了startindex变量,那么将从

startindex处开始查找。例如:

%stringfirstabdefabc

3

%stringfirstabdefabc4

-1

■4、stringindexstringcharIndex

返回string中第charIndex个字符(0-based)。charIndex可以是下面的值:

整数n:字符串中第n个字符(0-based)

end:最后一个字符

end-整数n:倒数第n个字符。stringindex"abcd"end-1返回字符'c'

如果charIndex小于0,或者大于字符串string的长度,那么返回空。

例如:

%stringindexabcdef2

c

%stringindexabcdefend-2

d

■5、stringlaststring1string2?startindex?

参照3.唯一的区别是从后往前查找

■6、stringlengthstring

返回字符串string的长度.

■7、stringmatch?-nocase?patternstring

如果pattern匹配string,那么返回1,否则返回0.如果有-nocase参数,那么就不区分大小写.

在pattern中可以使用通配符:

*匹配string中的任意长的任意字符串,包括空字符串.

匹配string中任意单个字符

[chars]匹配字符集合chars中给出的任意字符,其中可以使用A-Z这种形式

x匹配单个字符x,使用''是为了让x可以为字符*,-,[,].

例子:

%stringmatch*abcdef

1

%stringmatcha*abcdef

1

stringmatcha?cdefabcdef

1

%stringmatch{a[b-f]cdef}abcdef//注意一定药用'{',否则TCL解释器会把b-f当作命

令名

1//从而导致错误

%stringmatch{a[b-f]cdef}accdef

1

■8、stringrangestringfirstlast

返回字符串string中从第first个到第last个字符的子字符串(0-based)。如果first<0,那么

first被看作0,如果last大于或等于字符串的长度,那么last被看作end,如果first比last

大,那么返回空。

■9、stringrepeatstringcount

返回值为:重复了string字符串count次的字符串。例如:

%stringrepeat"abc"2

abcabc

■10、stringreplacestringfirstlast?newstring?

返回值为:从字符串string中删除了第first到第last个字符(0-based)的字符串,如果给出

了newstring变量,那么就用newstring替换从第first到第last个字符。如果first<0,那

么first被看作0,如果last大于或等于字符串的长度,那么last被看作end,如果first比last

大或者大于字符串string的长度或者last小于0,那么原封不动的返回string。

■11、stringtolowerstring?first??last?

返回值为:把字符串string转换成小写后的字符串,如果给出了first和last变量,就只转换

first和last之间的字符。

■12、stringtoupperstring?first??last?

同11。转换成大写。

■13、stringtrimstring?chars?

返回值为:从string字符串的首尾删除掉了字符集合chars中的字符后的字符串。如果没有给出

chars,那么将删除掉spaces、tabs、newlines、carriagereturns这些字符。例如:

%stringtrim"abcde"{ade}

bc

%stringtrim"def

>"

def

■14、stringtrimleftstring?chars?

同13。不过只删除左边的字符。

■15、stringtrimrightstring?chars?

同13。不过只删除右边的字符。

八文件访问

1文件名

TCL提供了丰富的文件操作的命令。通过这些命令你可以对文件名进行操作(查找匹配某一模式

的文件)、以顺序或随机方式读写文件、检索系统保留的文件信息(如最后访问时间)。

CL中文件名和我们熟悉的windows表示文件的方法有一些区别:在表示文件的目录结构时它

使用'/',而不是'',这和TCL最初是在UNIX下实现有关。比如C盘tcl目录下的文件

在TCL中这样表示:C:/tcl/。

2基本输入输出命令

这个名为tgrep的过程,可以说明TCL文件I/O的基本特点:

proctgrep{patternfilename}{

setf[open$filenamer]

while{[gets$fline]}{

if{[regexp$pattern$line]}{

putsstdout$line

}

}

close$f

}

以上过程非常象UNIX的grep命令,你可以用两个参数调用它,一个是模式,另一个是文件

名,tgrep将打印出文件中所有匹配该模式的行。

下面介绍上述过程中用到的几个基本的文件输入输出命令。

16、openname?access?

open命令以access方式打开文件name。返回供其他命令(gets,close等)使用的文件标识。

如果name的第一个字符是“|”,管道命令被触发,而不是打开文件。

文件的打开方式和我们熟悉的C语言类似,有以下方式:

r只读方式打开。文件必须已经存在。这是默认方式。

r+读写方式打开,文件必须已经存在。

w只写方式打开文件,如果文件存在则清空文件内容,否则创建一新的空文件。

w+读写方式打开文件,如文件存在则清空文件内容,否则创建新的空文件。

a只写方式打开文件,文件必须存在,并把指针指向文件尾。

a+只读方式打开文件,并把指针指向文件尾。如文件不存在,创建新的空文件。

open命令返回一个字符串用于表识打开的文件。当调用别的命令(如:gets,puts,close,〕

对打开的文件进行操作时,就可以使用这个文件标识符。TCL有三个特定的文件标识:

stdin,stdout和stderr,分别对应标准输入、标准输出和错误通道,任何时候你都可以使

用这三个文件标识。

(sat)28%setresultFileName"D:/Log/sat/S3760/"

D:/Log/sat/S3760/

OPEN执行完后产生一个标识:filefea148,供后面操作时作用。

(sat)29%open$resultFileNamea+

filefea148

(sat)30%setresultFile[open$resultFileNamea+]

filefe7188

(sat)33%puts$resultFile[format"%10s%7s%8s%6s%7s%8s%s"

测试例刘靖名线卡测试阶段测试结果测试用时"测试例名"]

(sat)34%flush$resultFile

通过FLUSH命令来对上面那个标识符进行输出操作。输出的结果写在RESULT文件中。

■17、getsfileId?varName?

读fileId标识的文件的下一行,忽略换行符。如果命令中有varName就把该行

赋给它,并返回该行的字符数(文件尾返回-1),如果没有varName参数,

返回文件的下一行作为命令结果(如果到了文件尾,就返回空字符串)。

sat)35%setfileId[opentc_]

filefe7a00

(sat)36%gets$fileIdline

579

(sat)37%puts$line

用例组名称,测试例序号,测试例描述,测试等级,测试拓扑名称,拓扑类型,SMB卡

类型,使用端口,测试用时,版本号,设计人,负责

人,S9600,S8600,S8600_V10.3,S8600_V10.4,S7600,S7600_V10.24,

S6800,S5760,S5760_V10.4,S5750,S5750_V10.4,S5750E,S5750E_V

10.4,S5750S,S3760,S3760_V10.4,S3760E_V10.4,S3750,S3750_V1

0.24,S3250,S3250_V10.24,S3250E_V10.4,S2924G_V10.4,S2924G_

V10.24,S2951XG,S2951XG_V10.24,S2952,S2600,S2600_V10.24,S2

600_V10.4,S2600E_V10.4,S2600P_V10.4,S2300,S2300_V10.24,S2

128,S2126,S2052_V10.24,S2026,S2026_V10.24,RSR50,RSR50E,RS

R30-44,RSR10,RSR20,RSR20-24,RSR20-24-ESW,RSR20-24_V1.73,

R2600,R2700,R2700-ESW,R2700-ESW_V1.73,R3740,NPE50,NPE80,

VPN120

和gets类似的命令是read,不过read不是以行为单位的,它有两种形式:

■18、read?-nonewline?

fileId读并返回fileId标识的文件中所有剩下的字节。如果没有nonewline开关,则在换行符

处停止。

■19readfileIdnumBytes

在fileId标识的文件中读并返回下一个numbytes字节。

这些输出的字符数

就是579

■20puts?-nonewline??fileId?stringputs

命令把string写到fileId中,如果没有nonewline开关的话,添加换行符。fileId默认是stdout。

命令返回值为一空字符串。

puts命令使用C的标准I/O库的缓冲区方案,这就意味着使用puts产生的信息不会立即出现

在目标文件中。如果你想使数据立即出现在文件中,那你就调用flush命令:

■21flushfileId

把缓冲区内容写到fileId标识的文件中,命令返回值为空字符串。

flush命令迫使缓冲区数据写到文件中。flush直到数据被写完才返回。当文件关闭时缓冲区数

据会自动flush。

■22close?fileId?

关闭标识为fileId的文件,命令返回值为一空字符串。

这里特别说明的一点是,TCL中对串口、管道、socket等的操作和对文件的操作类似,以上对

文件的操作命令同样适用于它们

3随机文件访问

默认文件输入输出方式是连续的:即每个gets或read命令返回的是上次gets或read访问位

置后面的字节,每个puts命令写数据是接着上次puts写的位置接着写。TCL提供了seek,tell

和eof等命令使用户可以非连续访问文件。

每个打开的打开文件都有访问点,即下次读写开始的位置。文件打开时,访问点总是被设置为文

件的开头或结尾,这取决于打开文件时使用的访问模式。每次读写后访问位置按访问的字节数后

移相应的位数。

可以使用seek命令来改变文件的访问点:

■23seekfileIdoffset?origin?

把fileId标识的文件的访问点设置为相对于origin偏移量为offset的位置。origin可以是start,

current,end,默认是start。命令的返回值是一空字符串。

例如:seekfileId2000改变fieleId标识的文件访问点,以便下次读写开始于文件的第2000

个字节。

■24seek

的第三个参数说明偏移量从哪开始计算。第三个参数必为start,current或end中的一个。start

是默认值:即偏移量是相对文件开始处计算。current是偏移量从当前访问位置计算。end是偏

移量从文件尾开始计算。

■25tellfileId

返回fileId标识的文件的当前访问位置。

■26eoffileId

如果到达fileId标识的文件的末尾返回1,否则返回0

4当前工作目录

TCL提供两个命令来管理当前工作目录:pwd和Cd。

pwd和UNIX下的pwd命令完全一样,没有参数,返回当前目录的完整路径。

cd命令也和UNIX命令也一样,使用一个参数,可以把工作目录改变为参数提供的目录。如果

cd没使用参数,UNIX下,会把工作目录变为启动TCL脚本的用户的工作目录,WINDOWS

下会把工作目录变为windows操作系统的安装目录所在的盘的根目录(如:C:/)。值得注意的

是,提供给cd的参数中路径中的应该用'/'而不是''。如cdC:/TCL/lib。这是UNIX的风格。

5文件操作和获取文件信息

TCL提供了两个命令进行文件名操作:glob和file,用来操作文件或获取文件信息。

glob命令采用一种或多种模式作为参数,并返回匹配这个(些)模式的所有文件的列表,其语

法为:

■27glob?switches?pattern?pattern...?

其中switches可以取下面的值:

-nocomplain:允许返回一个空串,没有-nocomplain时,如果结果是空的,就返回错误。

--:表示switches结束,即后面以'-'开头的参数将不作为switches。

glob命令的模式采用stringmatch命令(见8.5.7节)的匹配规则。例如:

%glob*.c*.h

.h

返回当前目录中所有.c或.h的文件名。glob还允许模式中包含'括在花括号中间以逗号分开

的多种选择',例如:

%glob{{src,backup}/*.[ch]}

src///p/hash.c

下面的命令和上面的命令等价:

glob{src/*.[ch]}{backup/*.[ch]}

注意:这些例子中模式周围的花括号是必须的,可以防止命令置换。在调用glob命令对应的C

过程前这些括号会被TCL解释器去掉。

如果glob的模式以一斜线结束,那将只匹配目录名。例如:

glob*/

只返回当前目录的所有子目录。

如果glob返回的文件名列表为空,通常会产生一个错误。但是glob的在样式参数之前的第一

个参数是"-nocomplain"的话,这时即使结果为空,glob也不会产生错误。

对文件名操作的第二个命令是file。file是有许多选项的常用命令,可以用来进行文件操作也

可以检索文件信息。这节讨论与名字相关的选项,下一节描述其他选项。使用file命令时,我们

会发现其中有很明显的UNIX痕迹。

fileatimename返回一个十进制的字符串,表示文件name最后被访问的时间。时间是以

秒为单位从1970年1月1日12:00AM开始计算。如果文件name不存在或查询不到访问

时间就返回错误。例:

%

975945600

■28filecopy?-force??--?sourcetarget

filecopy?-force??--?source?source...?targetDir

这个命令把source中指明的文件或目录递归的拷贝到目的地址targetDir,只有当存在-force

选项时,已经存在的文件才会被覆盖。试图覆盖一个非空的目录或以一个文件覆盖一个目录或以

一个目录覆盖一个文件都会导致错误。--的含义和前面所说的一样。

filedelete?-force??--?pathname?pathname...?这个命令删除pathname指定的

文件或目录,当指定了-force时,非空的目录也会被删除。即使没有指定-force,只读文件也

会被删除。删除一个不存在的文件不会引发错误。

filedirnamename返回name中最后一个“/”前的所有字符;如果name不包含“/”,返

回“.”;如果name中最后一个“/”是第name的第一个字符,返回“/”。

fileexecutablename如果name对当前用户是可以执行的,就返回1,否则返回0。

fileexistsname如果name存在于当前用户拥有搜索权限的目录下返回1,否则返回0。

fileextensionname返回name中最后的“.”以后(包括这个小数点)的所有字符。如果

name中没有“.”或最后斜线后没有“.”返回空字符。

fileisdirectoryname如果name是目录返回1,否则返回0。

fileisfilename如果name是文件返回1,否则返回0。

filelstatnamearrayName除了利用lstat内核调用代理stat内核调用之外,和filestat

命令一样,这意味着如果name是一个符号连接,那么这个命令返回的是这个符号连接的信息

而不是这个符号连接指向的文件的信息。对于不支持符号连接的操作系统,这个命令和和file

stat命令一样。

filemkdirdir?dir...?这个命令和UNIX的mkdir命令类似,创建dir中指明的目录。如

果dir已经存在,这个命令不作任何事情,也不返回错误。不过如果试图用一个目录覆盖已经存

在的一个文件会导致错误。这个命令顺序处理各个参数,如果发生错误的话,马上退出。

(sat)21%setrootpath"E:/Log/sat/S3760"

E:/Log/sat/S3760

(sat)22%filemkdir$rootpath

在E盘就可创建一个Log/sat/S3760文件夹

filemtimename返回十进制的字符串,表示文件name最后被修改的时间。时间是以秒为

单位从1970年1月1日12:00AM开始计算。

fileownedname如果name被当前用户拥有,返回1,否则返回0。

filereadablename如果当前用户可对name进行读操作,返回1,否则返回0。

filereadlinkname返回name代表的符号连接所指向的文件。如果name不是符号连接

或者找不到符号连接,返回错误。在不支持符号连接的操作系统(如windows)中选项readlink

没有定义。

filerename?-force??--?sourcetarget

filerename?-force??--?source?source...?targetDir

这个命令同时具有重命名和移动文件(夹)的功能。把source指定的文件或目录改名或移动到

targetDir下。只有当存在-force选项时,已经存在的文件才会被覆盖。试图覆盖一个非空的

目录或以一个文件覆盖一个目录或以一个目录覆盖一个文件都会导致错误。

filerootnamename返回name中最后“.”以前(不包括这个小数点)的所有字符。如果

name中没有“.”返回Name。

filesizename返回十进制字符串,以字节表示name的大小。如果文件不存在或得不到

name的大小,返回错误。

filestatnamearrayName调用stat内核来访问name,并设置arrayName参数来保存

stat的返回信息。arrayName被当作一个数组,它将有以下元素:atime、ctime、dev、gid、

ino、mode、mtime、nlink、size、type和uid。除了type以外,其他元素都是十进制的字

符串,type元素和filetype命令的返回值一样。其它各个元素的含义如下:

atime最后访问时间.

ctime状态最后改变时间.

dev包含文件的设备标识.

gid文件组标识.

ino设备中文件的序列号.

mode文件的mode比特位.

mtime最后修改时间.

nlink到文件的连接的数目.

size按字节表示的文件尺寸.

uid文件所有者的标识.

这里的atime、mtime、size元素与前面讨论的file的选项有相同的值。要了解其他元素更多

的信息,就查阅stat系统调用的文件;每个元都直接从相应stat返回的结构域中得到。文件

操作的stat选项提供了简单的方法使一次能获得一个文件的多条信息。这要比分多次调用file

来获得相同的信息量要显著的快。

filetailname返回name中最后一个斜线后的所有字符,如果没有斜线返回name。

filetypename返回文件类型的字符串,返回值可能是下列中的一个:file、directory、

characterspecial、blockSpecial、fifo、link或socket。

filewritablename

如果当前用户对name可进行写操作,返回1,否则返回0。

九错误和异常

错误和异常处理机制是创建大而健壮的应用程序的必备条件之一,很多计算机语言都提供了错误

和异常处理机制,TCL也不例外。

错误(Errors)可以看作是异常(Exceptions)的特例。TCL中,异常是导致脚本被终止的事件,

除了错误还包括break、continue和return等命令。TCL允许程序俘获异常,这样仅有程序

的一部分工作被撤销。程序脚本俘获异常事件以后,可以忽略它,或者从异常中恢复。如果脚本

无法恢复此异常,可以把它重新发布出去。下面是与异常有关的TCL命令:

■29catchcommand?varName?

这个命令把command作为TCL脚本求值,返回一个整型值表明command结束的状态。如

果提供varName参数,TCL将生成变量varName,用于保存command产生的错误消息。

■30errormessage?info??code?

这个命令产生一个错误,并把message作为错误信息。如果提供info参数,则被用于初始化

全局变量errorInfo。如果提供code参数,将被存储到全局变量errorCode中。

■31return-codecode?-errorinfoinfo??-errorcodeerrorCode??string?

这个命令使特定过程返回一个异常。code指明异常的类型,必须是

ok,error,return,break,continue或者是一个整数。-errorinfo选项用于指定全局变量

errorInfo的初始值,-errorcode用于指定全局变量errorCode的初始值。string给出return

的返回值或者是相关的错误信息,其默认值为空。

1错误

当发生一个TCL错误时,当前命令被终止。如果这个命令是一大段脚本的一部分,那么整个脚

本被终止。如果一个TCL过程在运行中发生错误,那么过程被终止,同时调用它的过程,以至

整个调用栈上的活动过程都被终止,并返回一个错误标识和一段错误描述信息。

举个例子,考虑下面脚本,它希望计算出列表元素的总和:

setlist{44161239857}

setsum0

foreachel$list{

setsum[expr$sum+$element]

}

=>can'tread"element":nosuchvariable

这个脚本是错误的,因为没有element这个变量。TCL分析expr命令时,会试图用element

变量的值进行替换,但是找不到名字为element的变量,所以会报告一个错误。由于foreach

命令利用TCL解释器解释循环体,所以错误标识被返回给foreach。foreach收到这个错误,

会终止循环的执行,然后把同样的错误标识作为它自己的返回值返回给调用者。按这样的顺序,

将致使整个脚本终止。错误信息can'tread"element":nosuchvariable会被一路返回,并

且很可能被显示给用户。

很多情况下,错误信息提供了足够的信息为你指出哪里以及为什么发生了错误。然而,如果错误

发生在一组深层嵌套的过程调用中,仅仅给出错误信息还不能为指出哪里发生了错误提供足够信

息。为了帮助我们指出错误的位置,当TCL撤销程序中运行的命令时,创建了一个跟踪栈,并

且把这个跟踪栈存储到全局变量errorInfo中。跟踪栈中描述了每一层嵌套调用。例如发生上面

的那个错误后,errorInfo有如下的值:

can'tread"element":nosuchvariable

whileexecuting

"expr$sum+$element"

("foreach"bodyline2)

invokedfromwithin

"foreachel$list{

setsum[expr$sum+$element]

}"

在全局变量errorCode中,TCL还提供了一点额外的信息。errorCode变量是包含了一个或若

干元素的列表。第一个元素标示了错误类别,其他元素提供更详细的相关的信息。不过,

errorCode变量是TCL中相对较新的变量,只有一部分处理文件访问和子过程的命令会设置这

个变量。如果一个命令产生的错误没有设置errorCode变量,TCL会填一个NONE值。

当用户希望得到某一个错误的详细的信息,除了命令返回值中的错误信息外,可以查看全局变

量errorInfo和errorCode的值。

2从TCL脚本中产生错误

大多数TCL错误是由实现TCL解释器的C代码和内建命令的C代码产生的。然而,通过执行

TCL命令error产生错误也是可以的,见下面的例子:

if{($x100)}{

error"xisoutofrange($x)"

}

error命令产生了一个错误,并把它的参数作为错误消息。

作为一种编程的风格,你应该只在迫不得已终止程序时下才使用error命令。如果你认为错误很

容易被恢复而不必终止整个脚本,那么使用通常的return机制声明成功或失败会更好(例如,

命令成功返回某个值,失败返回另一个值,或者设置变量来表明成功或失败)。尽管从错误中恢

复是可能的,但恢复机制比通常的return返回值机制要复杂。因此,最好是在你不想恢复的情

况下才使用error命令。

3使用catch捕获错误

错误通常导致所有活动的TCL命令被终止,但是有些情况下,在错误发生后继续执行脚本是有

用的。例如,你用unset取消变量x的定义,但执行unset时,x可能不存在。如果你用unset

取消不存在的变量,会产生一个错误:

%unsetx

can'tunset"x":nosuchvariable

此时,你可以用catch命令忽略这个错误:

%catch{unsetx}

1

catch的参数是TCL脚本。如果脚本正常完成,catch返回0。如果脚本中发生错误,catch

会俘获错误(这样保证catch本身不被终止掉)然后返回1表示发生了错误。上面的例子忽略

unset的任何错误,这样如果x存在则被取消,即使x以前不存在也对脚本没有任何影响。

catch命令可以有第二个参数。如果提供这个参数,它应该是一个变量名,catch把脚本的返回

值或者是出错信息存入这个变量。

%catch{unsetx}msg

1

%setmsg

can'tunset"x":nosuchvariable

在这种情况下,unset命令产生错误,所以msg被设置成包含了出错信息。如果变量x存在,

那么unset会成功返回,这样catch的返回值为0,msg存放unset命令的返回值,这里是个

空串。如果在命令正常返回时,你想访问脚本的返回值,这种形式很有用;如果你想在出错时利

用错误信息做些什么,如产生log文件,这种形式也很有用。

4其它异常

错误不是导致运行中程序被终止的唯一形式。错误仅是被称为异常的一组事件的一个特例。除了

error,TCL中还有三种形式的异常,他们是由break、continue和return命令产生的。所有

的异常以相同的方式导致正在执行的活动脚本被终止,但有两点不同:首先,errorInfo和

errorCode只在错误异常中被设置;其次,除了错误之外的异常几乎总是被一个命令俘获,不

会波及其他,而错误通常撤销整个程序中所有工作。例如,break和continue通常是被引入到

一个如foreach的循环命令中;foreach将俘获break和continue异常,然后终止循环或者

跳到下一次重复。类似地,return通常只被包含在过程或者被source引入的文件中。过程实

现和source命令将俘获return异常。

所有的异常伴随一个字符串值。在错误情况,这个串是错误信息,在return方式,串是过程或

脚本的返回值,在break和continue方式,串是空的。

catch命令其实可以俘获所有的异常,不仅是错误。catch命令的返回值表明是那种情况的异常,

catch命令的第二个参数用来保存与异常相关的串。例如:

%catch{return"alldone"}string

2

%setstring

alldone

下表是对命令:catchcommand?varName?的说明。

catch返回值描述俘获者

0

正常返回,varName给出返回值无异常

1

错误。varName给出错误信息

catch

2

执行了return命令,varName包含过程返回值

或者返回给source的结果

catch,source,过程调用

3

执行了break命令,varName为空catch,for,foreach,while,过程

4

执行了continue命令,varName为空catch,for,foreach,while,过程

其他值用户或应用自定义

catch

与catch命令提供俘获所有异常的机制相对应,return可以提供产生所有类型异常。

这里有一个do命令的实现,使用了catch和return来正确处理异常:

procdo{varNamefirstlastbody}{

globalerrorInfoerrorCode

upvar$varNamev

for{setv$first}{$v<=$last}{incrv}{

switch[catch{uplevel$body}string]{

1{return-codeerror-errorInfo$errorInfo

-errorcode$errorCode$string}

2{return-codereturn$string}

3return

}

}

}

这个新的实现在catch命令中求循环体的值,然后检查循环体是如何结束的。如果没有发生异

常(0),或者异常是continue(4),那么do继续下一个循环。如果发生error(1)或者return(2),

那么do使用return把异常传递到调用者。如果发生了break(3)异常,那么do正常返回到调

用者,循环结束。

当do反射一个error到上层时,它使用了return的-errorInfo选项,保证错误发生后能够得

到一个正确的调用跟踪栈。-errorCode选项用于类似的目的以传递由catch命令得到的初始

errorCode,作为do命令的errorCode返回。如果没有-errorCode选项,errorCode变量

总是得到NONE值。

十深入TCL

1查询数组中的元素

利用array命令可以查询一个数组变量中已经定义了的元素的信息。array命令的形式如下:

arrayoptionarrayName?argarg...?

由于option的不同,array命令有多种形式。

如果我们打算开始对一个数组的元素进行查询,我们可以先启动一个搜索(search),这可以由

下面的命令做到:

arraystartseracharrayName这个命令初始化一个对name数组的所有元素的搜索

(search),返回一个搜索标识(searchidentifier),这个搜索标识将被用于命令array

nextelement、arrayanymore和arraydonesearch。

arraynextelementarrayNamesearchId这个命令返回arrayName的下一个元素,如

果arrayName的所有元素在这一次搜索中都已经返回,那么返回一个空字符串。搜索标识

searchId必须是arraystartserach的返回值。注意:如果对arrayName的元素进行了添

加或删除,那么所有的搜索都会自动结束,就象调用了命令arraydonesearch一样,这样

会导致arraynextelement操作失败。

arrayanymorearrayNamesearchId如果在一个搜索中还有元素就返回1,否则返回0。

searchId同上。这个命令对具有名字为空的元素的数组尤其有用,因为这时从array

nextelement中不能确定一个搜索是否完成。

arraydonesearcharrayNamesearchId这个命令中止一个搜索,并销毁和这个搜索有关

的所有状态。searchId同上。命令返回值为一个空字符串。当一个搜索完成时一定要注意调用

这个命令。

array命令的其他option如下:

arrayexistsarrayName如果存在一个名为arrayName的数组,返回1,否则返回0。

arraygetarrayName?pattern?这个命令的返回值是一个元素个数为偶数的的list。我们

可以从前到后把相邻的两个元素分成一个个数据对,那么,每个数据对的第一个元素是

arrayName中元素的名字,数据对的第二个元素是该数据元素的值。数据对的顺序没有规律。

如果没有pattern参数,那么数组的所有元素都包含在结果中,如果有pattern参数,那么只

有名字和pattern匹配(用stringmatch的匹配规则)的元素包含在结果中。如果arrayName

不是一个数组变量的名字或者数组中没有元素,那么返回一个空list。例:

%setb(first)1

1

%setb(second)2

2

%arraygetb

second2first1

arraysetarrayNamelist设置数组arrayName的元素的值。list的形式和arrayget

的返回值的list形式一样。如果arrayName不存在,那么生成arrayName。例:

%arrayseta{first1second2}

%puts$a(first)

1

%arraygeta

second2first1

arraynamesarrayName?pattern?这个命令返回数组arrayName中和模式pattern匹

配的元素的名字组成的一个list。如果没有pattern参数,那么返回所有元素。如果数组中没有

匹配的元素或者arrayName不是一个数组的名字,返回一个空字符串。

arraysizearrayName返回代表数组元素个数的一个十进制的字符串,如果arrayName

不是一个数组的名字,那么返回0。

下面这个例子通过使用arraynames和foreach命令,枚举了数组所有的元素:

foreachi[arraynamesa]{

puts"a($i)=$a($i)"

}

当然,我们也可以利用startsearch、anymore、nextelement、和donesearch选项

来遍历一个数组.这种方法比上面所给出的foreach方法的效率更高,不过要麻烦得多,因此

不常用。

2info命令

info命令提供了查看TCL解释器信息的手段,它有超过一打的选项,详细说明请参考下面几

节。

■变量信息

■32Info

命令的几个选项提供了查看变量信息的手段。

infoexistsvarName如果名为varName的变量在当前上下文(作为全局或局部变量)存在,

返回1,否则返回0。

infoglobals?pattern?如果没有pattern参数,那么返回包含所有全局变量名字的一个list。

如果有pattern参数,就只返回那些和pattern匹配的全局变量(匹配的方式和stringmatch

相同)。

infolocals?pattern?如果没有pattern参数,那么返回包含所有局部变量(包括当前过程的

参数)名字的一个list,global和upvar命令定义的变量将不返回。如果有pattern参数,就只

返回那些和pattern匹配的局部变量(匹配的方式和stringmatch相同)。

infovars?pattern?如果没有pattern参数,那么返回包括局部变量和可见的全局变量的名

字的一个list。如果有pattern参数,就只返回和模式pattern匹配的局部变量和可见全局变

量。模式中可以用namespace来限定范围,如:foo::option*,就只返回namespace中和

option*匹配的局部和全局变量。(注:tcl80以后引入了namespace概念,不过我们一般编

写较小的TCL程序,可以对namespace不予理睬,用兴趣的话可以查找相关资料。)

下面针对上述命令举例,假设存在全局变量global1和global2,并且有下列的过程存在:

proctest{arg1arg2}{

globalglobal1

setlocal11

setlocal22

...

}

然后在过程中执行下列命令:

%infovars

global1arg1arg2local2local1//global2不可见

%infoglobals

global2global1

%infolocals

arg1arg2local2local1

%infovars*al*

global1local2local1

■过程信息

info命令的另外的一些选项可以查看过程信息。

infoprocs?pattern?如果没有pattern参数,命令返回当前namespace中定义的所有过

程的名字。如果有pattern参数,就只返回那些和pattern匹配的过程的名字(匹配的方式和

stringmatch相同)。

infobodyprocname返回过程procname的过程体。procname必须是一个TCL过程。

infoargsprocname返回包含过程procname的所有参数的名字的一个list。procname

必须是一个TCL过程。

infodefaultprocnameargvarnameprocname必须是一个TCL过程,arg必须是这个

过程的一个变量。如果arg没有缺省值,命令返回0;否则返回1,并且把arg的缺省值赋给

变量varname。

infolevel?number?如果没有number参数,这个命令返回当前过程在调用栈的位置。如

果有number参数,那么返回的是包含在调用栈的位置为number的过程的过程名及其参数

的一个list。

下面针对上述命令举例:

procmaybeprint{ab{c24}}{

if{$a<$b}{

putsstdout"cis$c"

}

}

%infobodymaybeprint

if{$a<$b}{

putsstdout"cis$c"

}

%infoargsmaybeprint

abc

%infodefaultmaybeprintax

0

%infodefaultmaybeprintac

1

%setx

24

下面的过程打印出了当前的调用栈,并显示了每一个活动过程名字和参数:

procprintStack{}{

setlevel[infolevel]

for{seti1}{$i<$level}{incri}{

puts"Level$i:[infolevel$i]"

}

}

■33glob

匹配文件名。形式:glob?flags?pattern?pattern?

*匹配0个或多个字符

?匹配单个字符

[abc]匹配一组字符

{a,b,c}匹配a,b,c中任一个。

(sat)23%setrootpath"D:/Log/sat/S3760"

D:/Log/sat/S3760

(sat)24%glob-directory$rootpath*_

D:/Log/sat/S3760/2009-08-21_10-04-57_

D:/Log/sat/S3760/2009-08-21_11-29-49_

D:/Log/sat/S3760/2009-09-01_14-29-07_

如上目录下存在匹配_文件。不存在则返回空。

把那些文件删除则结果如下:

(sat)26%glob-directory$rootpath*_

nofilesmatchedglobpattern"*_"

■命令信息

info命令的另外选项可以查看命令信息。

infocommands?pattern?如果没有参数pattern,这个命令返回包含当前namspace中

所有固有、扩展命令以及以proc命令定义的过程在内的所有命令的名字的一个list。pattern

参数的含义和infoprocs一样。

infocmdcount返回了一个十进制字符串,表明多少个命令曾在解释器中执行过。

infocompletecommand如果命令是command完整的,那么返回1,否则返回0。这里

判断命令是否完整仅判断引号,括号和花括号是否配套。

infoscript如果当前有脚本文件正在Tcl解释器中执行,则返回最内层处于激活状态的脚本

文件名;否则将返回一个空的字符串。

■TCL的版本和库

infotclversion返回为Tcl解释器返回的版本号,形式为,例如8.3。

infolibrary返回Tcl库目录的完全路径。这个目录用于保存Tcl所使用的标准脚本,TCL在

初始化时会执行这个目录下的脚本。

■命令的执行时间

TCL提供time命令来衡量TCL脚本的性能:

timescript?count?这个命令重复执行script脚本count次。再把花费的总时间的用count

除,返回一次的平均执行时间,单位为微秒。如果没有count参数,就取执行一次的时间。

■跟踪变量

TCL提供了trace命令来跟踪一个或多个变量。如果已经建立对一个变量的跟踪,则不论什么

时候对该变量进行了读、写、或删除操作,就会激活一个对应的Tcl命令,跟踪可以有很多的用

途:

1.监视变量的用法(例如打印每一个读或写的操作)。

2.把变量的变化传递给系统的其他部分(例如一个TK程序中,在一个小图标上始终显示某个变

量的当前值)。

3.限制对变量的某些操作(例如对任何试图用非十进制数的参数来改变变量的值的行为产生一个

错误。)或重载某些操作(例如每次删除某个变量时,又重新创建它)。

trace命令的语法为:

traceoption?argarg...?

其中option有以下几种形式:

tracevariablenameopscommand这个命令设置对变量name的一个跟踪:每次当对

变量name作ops操作时,就会执行command命令。name可以是一个简单变量,也可以

是一个数组的元素或者整个数组。

ops可以是以下几种操作的一个或几个的组合:

r当变量被读时激活command命令。

w当变量被写时激活command命令。

u当变量被删除时激活command命令。通过用unset命令可以显式的删除一个变量,一个过

程调用结束则会隐式的删除所有局部变量。当删除解释器时也会删除变量,不过这时跟踪已经不

起作用了。

当对一个变量的跟踪被触发时,TCL解释器会自动把三个参数添加到命令command的参数列

表中。这样command实际上变成了

commandname1name2op

其中op指明对变量作的什么操作。name1和name2用于指明被操作的变量:如果变量是一

个标量,那么name1给出了变量的名字,而name2是一个空字符串;如果变量是一个数组的

一个元素,那么name1给出数组的名字,而name2给出元素的名字;如果变量是整个数组,

那么name1给出数组的名字而name2是一个空字符串。为了让你很好的理解上面的叙述,下

面举一个例子:

tracevariablecolorwpvar

tracevariablea(length)wpvar

procpvar{nameelementop}{

if{$element!=""}{

setname${name}($element)

}

upvar$namex

puts"Variable$namesetto$x"

}

上面的例子中,对标量变量color和数组元素a(length)的写操作都会激活跟踪操作pvar。我

们看到过程pvar有三个参数,这三个参数TCL解释器会在跟踪操作被触发时自动传递给pvar。

比如如果我们对color的值作了改变,那么激活的就是pvarcolor""w。我们敲入:

%setcolorgreen

Variablecolorsettogreen

green

command将在和触发跟踪操作的代码同样的上下文中执行:如果对被跟踪变量的访问是在一

个过程中,那么command就可以访问这个过程的局部变量。比如:

procHello{}{

seta2

tracevariablebw{puts$a;list}

setb3

}

%Hello

2

3

对于被跟踪变量的读写操作,command是在变量被读之后,而返回变量的值之前被执行的。

因此,我们可以在command对变量的值进行改变,把新值作为读写的返回值。而且因为在执

行command时,跟踪机制会临时失效,所以在command中对变量进行读写不会导致

command被递归激活。例如:

%tracevariablebrtmp

%proctmp{var1var2var3}{

upvar$var1t

incrt1

}

%setb2

2

%puts$b

3

%puts$b

4

如果对读写操作的跟踪失败,即command失败,那么被跟踪的读写操作也会失败,并且返回

和command同样的失败信息。利用这个机制可以实现只读变量。下面这个例子实现了一个值

只能为正整数的变量:

tracevariablesizewforceInt

procforceInt{nameelementop}{

upvar$namex

if![regexp{^[0-9]*$}$x]{

error"valuemustbapostiveinteger"

}

}

如果一个变量有多个跟踪信息,那么各个跟踪被触发的先后原则是:最近添加的跟踪最先被触发,

如果有一个跟踪发生错误,后面的跟踪就不会被触发。

tracevdeletenameopscommand删除对变量name的ops操作的跟踪。返回值为一个

空字符串。

tracevinfoname这个命令返回对变量的跟踪信息。返回值是一个list,list的每个元素是

一个子串,每个子串包括两个元素:跟踪的操作和与操作关联的命令。如果变量name不存在

或没有跟踪信息,返回一个空字符串。

■命令的重命名和删除

rename命令可以用来重命名或删除一个命令。

renameoldNamenewName把命令oldName改名为newName,如果newName为空,

那么就从解释器中删除命令oldName。

下面的脚本删除了文件I/O命令:

foreachcmd{openclosereadgetsputs}{

rename$cmd{}

}

任何一个Tcl命令都可以被重命名或者删除,包括内建命令以及应用中定义的过程和命令。重命

名一个内建命令可能会很有用,例如,exit命令在Tcl中被定义为立即退出过程。如果某个应用

希望在退出前获得清除它内部状态的机会,那么可以这样作:

procexitstatus{

application-specificcleanup

...

$status

}

在这个例子中,exit命令被重命名为,并且定义了新的exit命令,这个新命令作了应

用必需的清除工作而后调用了改了名字的exit命令来结束进程。这样在已存在的描述程序中调

用exit时就会有机会做清理应用状态的工作。

■unknown命令

unknown命令的语法为:

unknowncmdName?argarg...?当一个脚本试图执行一个不存在的命令时,TCL解释器

会激活unknown命令,并把那个不存在的命令的名字和参数传递给unknown命令。

unknown命令不是TCL的核心的一部分,它是由TCL脚本实现的,可以在TCL安装目录的

lib子目录下的文件中找到其定义。

unknown命令具有以下功能:

1。如果命令是一个在TCL的某个库文件(这里的库文件指的是TCL目录的lib子目录下的TCL

脚本文件)中定义的过程,则加载该库并重新执行命令,这叫做“auto-loading”(自动加载),

关于它将在下一节描述。

2。如果存在一个程序的名字与未知命令一致,则调用exec命令来调用该程序,这项特性叫做

“auto-exec”(自动执行)。例如你输入“dir”作为一个命令,unknown会执行“execdir”来列

出当前目录的内容,如果这里的命令没有特别指明需要输入输出重定向,则自动执行功能会使用

当前Tcl应用所拥有的标准输入输出流,以及标准错误流,这不同于直接调用exec命令,但是

提供了在Tcl应用中直接执行其他应用程序的方法。

3。如果命令是一组特殊字符,将会产生一个新的调用,这个调用的内容是历史上已经执行过的

命令。例如,如果命令时“!!”则上一条刚执行过的命令会再执行一遍。下一章将详细讲述该功能。

4。若命令是已知命令的唯一缩写,则调用对应的全名称的正确命令。在TCL中允许你使用命令

名的缩写,只要缩写唯一即可。

如果你不喜欢unknown的缺省的行为,你也可以自己写一个新版本的unknown或者对库中

已有unknown的命令进行扩展以增加某项功能。如果你不想对未知命令做任何处理,也可以

删除unknown,这样当调用到未知命令的时候就会产生错误。

■自动加载

在unknown过程中一项非常有用的功能就是自动加载,自动加载功能允许你编写一组Tcl过程

放到一个脚本文件中,然后把该文件放到库目录之下,当程序调用这些过程的时候,第一次调用

时由于命令还不存在就会进入unknown命令,而unknown则会找到在哪个库文件中包含了

这个过程的定义,接着会加载它,再去重新执行命令,而到下次使用刚才调用过的命令的时候,

由于它已经存在了,从而会正常的执行命令,自动加载机制也就不会被再次启动。

自动加载提供了两个好处,首先,你可以把有用的过程建立为过程库,而你无需精确知道过程的

定义到底在哪个源文件中,自动加载机制会自动替你寻找,第二个好处在于自动加载是非常有效

率的,如果没有自动加载机制你将不得不在TCL应用的开头使用source命令来加载所有可能

用到的库文件,而应用自动加载机制,应用启动时无需加载任何库文件,而且有些用不到的库文

件永远都不会被加载,既缩短了启动时间又节省了内存。

使用自动加载只需简单的按下面三步来做:

第一,在一个目录下创建一组脚本文件作为库,一般这些文件都以".tcl"结尾。每个文件可以包

含任意数量的过程定义。建议尽量减少各脚本文件之间的关联,让相互关联的过程位于同一个文

件中。为了能够让自动加载功能正确运行,proc命令定义一定要顶到最左边,并且与函数名用

空格分开,过程名保持与proc在同一行上。

第二步,为自动加载建立索引。启动Tcl应用比如tclsh,调用命令auto_mkindexdir

pattern,第一个参数是目录名,第二个参数是一个模式。auto_mkindex在目录dir中扫描

文件名和模式pattern匹配的文件,并建立索引以指出哪些过程定义在哪些文件中,并把索引

保存到目录dir下一个叫tclindex的文件中。如果修改了文件或者增减过程,需要重新生成索

引。

第三步是在应用中设置变量auto_path,把存放了希望使用到的库所在的目录赋给它。

auto_path变量包含了一个目录的列表,当自动加载被启动的时候,会搜索auto_path中所指

的目录,检查各目录下的tclindex文件来确认过程被定义在哪个文件中。如果一个函数被定义

在几个库中,则自动加载使用在auto_path中靠前的那个库。

例如,若一个应用使用目录/usr/local/tcl/lib/shapes下的库,则在启动描述中应增加:

setauto_path[linsert$auto_path0/usr/local/tcl/lib/shapes]

这将把/usr/local/tcl/lib/shapes作为起始搜索库的路径,同时保持所有的Tcl/Tk库不变,但

是在/usr/local/tcl/lib/shapes中定义的过程具有更高的优先级,一旦一个含有索引的目录加

到了auto_path中,里面所有的过程都可以通过自动加载使用了。

👁️ 阅读量:0