陈小蒙毁灭记 完:tcl语言

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 03:12:02

>>2009-10-17 11:30:00

 

 

 

Tcl语言参考

Tcl("Tool Command Language",即工具命令语言;Tcl念作“踢叩” "tickle" )是一种易学易用易于扩展的脚本语言,实际上包

含了两个部分:一个语言和一个库。

首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些交互程序如文本编辑器、调试器和shell。它有一个简单

的语法和很强可扩充性,Tcl可以创建新的过程以增强其内建命令的能力。

其次,Tcl是一个库包,可以被嵌入应用程序,Tcl的库包含了一个分析器、用于执行内建命令的例程和可以使你扩充

(定义新的过程)的库函数。应用程序可以产生Tcl命令并执行,命令可以由用户产生,也可以从用户接口的一个输入中读

取(按钮或菜单等)。

 

Tcl和其他编程语言例如C不同,它是一种解释语言而非编译语言。Tcl程序由一系列Tcl命令组成,在运行时由Tcl解释

器解释运行。

 

  Tcl有大量的用C/C++编写的扩展用于提供Tcl本身不具有的功能。其中使用最广泛的扩展是TK,TK提供了各种OS平台下

的图形用户界面GUI(连强大的Python语言都不单独提供自己的GUI,而是提供接口适配到TK上)。另一个流行的扩展包是Exp-

ect,它提供了通过终端自动执行命令的能力,例如passwd, ftp, telnet等命令驱动的外壳。

 

 

一、Tcl程序基本结构

 

1、基本语法

 

Tcl有类似于shell的语法,一条Tcl的命令串包含了一条或多条命令用换行符或分号来隔开,而每一条命令包含了一个

域(field)的集合,域使用空白(空格或TAB)分开的,第一个域是一个命令的名字,其它的是作为参数来传给它。

Tcl解释器对一个命令的求值过程分为两部分:分析和执行。在分析阶段,Tcl 解释器运用规则识别出命令并把命令分

成一个个独立的单词,同时进行必要的置换(substitution); 在执行阶段,Tcl 解释器会把第一个单词当作命令名,并查看这

个命令是否有定义,如果有定义就激活这个命令对应的C/C++过程,并把所有的单词作为参数传递给该命令过程,让命令过

程进行处理。

 

Tcl的命令名可以是内建的命令也可以是用户建的新命令,在应用程序中可用函数Tcl_CreateCommand来创建新的命令。

所有的参数作为字符串来传递,命令自己会按其所需来解释的参数的。

另外关于Tcl的注释有一个特殊的要求:'#'必须出现在Tcl解释器期望命令的第一个字符出现的地方,才被当作注释。

例如:

set a 100 # Not a comment

set b 101 ; # this is a comment

第一个例子中'#'及其后面的内容会被当作set 的参数而非注释从而导致参数个数错误;但第二个被解释为注释,因为

'#'前面有一个分号,而分号和换行一样被看作是命令的分隔符。

 

脚步命令:

(1)eval命令

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

eval arg arg ...

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

(2)source命令

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

source e:/tcl&c/hello.tcl

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

2、置换(Substitution)

 

set x 10

set y 100 + x

上面命令执行后,y的值是“100 + x”而不是我们期望的110。这是因为Tcl解释器在分析命令时,把所有的命令参数都当作

字符串看待,所以x 被看作了字符串“100 + x”的一部分。如果我们想使用x的值'10' ,就必须告诉Tcl解释器:我们在这里期望的

是变量x的值,而非字符'x'。怎么告诉Tcl解释器呢,这就要用到Tcl语言中提供的置换功能。

Tcl提供三种形式的置换:变量置换、命令置换和反斜杠置换。每种置换都会导致一个或多个单词本身被其他的值所代替。置换

可以发生在包括命令名在内的每一个单词中,而且置换可以嵌套。

 

(1)变量置换(variable substitution)

变量置换由一个$符号标记,变量置换会导致变量的值而非变量(标识符)本身被插入到字符串中。

set x 10

set y 100 + $x

这时,y的值还不是我们想要的值110,而是10+100,因为Tcl解释器把10+100看成是一个字符串而不是表达式;y要想得到值

110,还必须用命令置换,使得Tcl会把10+100看成一个表达式并求值。

 

(2)命令置换(command substitution)

命令置换是由[]括起来的Tcl命令及其参数,命令置换会导致某一个命令的所有或部分单词(参数)被另一个命令的结果所代替。

set x 10

set y [expr 100+$x]

这时,y的值就是110了。这里当Tcl解释器遇到字符'['时,它就会把随后的expr作为一个命令名,从而激活与expr对应的C/C++过

程,并把expr命令中变量置换后得到的'10+110'传递给该命令过程进行处理。

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

 

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

(3)反斜杠置换(backslash subtitution)

Tcl语言中的反斜杠置换类似于C语言中反斜杠的用法,主要用于在单词符号中插入诸如换行符、空格、[、$等被Tcl解释器当作

特殊符号对待的字符。例如:

set msg multiple\ space 

如果没有'\'的话,Tcl会报错,因为解释器会把这里最后两个单词之间的空格认为是分隔符,于是发现set命令有多于两个参数,

从而报错。加入了'\'后,空格不被当作分隔符,'multiple space'被认为是一个单词(word)。

 

(4)双引号和花括号

除了使用反斜杠外,Tcl提供另外两种方法来使得解释器把分隔符和置换符等特殊字符当作普通字符,而不作特殊处理,这就

要使用双引号和花括号({})。

Tcl解释器对双引号中的各种分隔符将不作处理,但是对换行符及$和[]两种置换符会照常处理。而在花括号中,所有特殊字符

都将成为普通字符,失去其特殊意义,Tcl解释器不会对其作特殊处理。

 

 

二、Tcl变量数据类型

 

Tcl只支持一种数据类型:字符串(string)。所有的命令,命令的所有的参数,命令的结果,所有的变量都是字符串。请牢记

这一点,所有的东西都是字符串。(然而字符串的实际解释是依赖于其所处上下文。它有三种形式:命令, 表达式和表)。

 

Tcl变量根据其结构的复杂程度分为“简单变量”和“数组变量”两类。

(1)简单变量

一个Tcl的简单变量包含两个部分:名字和值,其中名字和值都可以是任意字符串。不过为了更好的使用变量置换功能,变量名

最好按C\C++语言中标识符的命名规则命名。这是因为Tcl解释器在分析一个变量置换时,只把从$符号往后直到第一个不是字母、数

字或下划线的字符之间的单词符号作为要被置换的变量的名字。例如:

set a 2

set a.1 5

set b $a.1

在最后一个命令行,我们希望把变量a.1的值付给b,但是Tcl解释器在分析时只把$符号之后直到第一个不是字母、数字或下划线

的字符(这里是'.')之间的单词符号(这里是'a')当作要被置换的变量的名字,所以Tcl解释器把a置换成2,然后把字符串“2.1”付给变量b。这

显然与我们的初衷不同。

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

set b ${a.1}

(2)数组变量

数组是一些元素的集合。Tcl的数组和一般编程语言中的数组有很大的区别。在Tcl中,不能单独声明一个数组,数组只能和数组

元素一起声明。数组中数组元素的名字包含两部分:数组名和数组中元素的名字,Tcl中数组元素的名字(下标〕可以为任何字符串。

例如:

set day(monday) 1 

set day(tuesday) 2

 

(3)重用结构及其操作

1、string结构及其操作

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

【format】

语法:format formatstring vlue value...

format命令类似于ANSIC中的sprintf函数,它按formatstring提供的格式,把各个value的值组合到formatstring中形成一

个新字符串,并返回。例如:

set msg [format "%s is %d years old" $name $age] 

【scab】

语法:scan string formatsting varName varName ...

scan命令可以认为是format命令的逆,其功能类似于ANSI C中的sscanf函数。它按formatsting提供的格式分析string字

符串,然后把结果存到变量varName中,注意除了空格和TAB键之外,string 和formatsting中的字符和'%'必须匹配。例如:

scan "some 26 34" "some %d %d" a b

【regexp】

语法:regexp [switchs] [--] exp string [matchVar]\ [subMatchVar subMatchVar...]

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

regexp可以设置一些开关(switchs〕,来控制匹配的具体方式,如:-nocase,-line等,其中-- 表示这后面再没有

开关(switchs〕了,即使后面有以'-'开头的参数也被当作正规表达式的一部分。

如果regexp命令后面有参数matchVar和subMatchVar,则所有的参数被当作变量名,如果变量不存在,就会被生成。 

regexp把匹配整个正规表达式的子字符串赋给第一个变量,匹配正规表达式的最左边的子表达式的子字符串赋给第二个变

量,依次类推,例如:

regexp { ([0-9]+) *([a-z]+)} " there is 100 apples" total num word 1 

puts " $total ,$num,$word" 

显示结果为:100 apples ,100,apples

其他具体的正则表达式规则请参考Tcl语言手册

【regsub】

语法:regsub [switchs] exp string subSpec varname

regsub的第一个参数是一个整个表达式,第二个参数是一个输入字符串,这一点和regexp命令完全一样,也是当匹配

时返回1,否则返回0。不过regsub用第三个参数的值来替换字符串string中和正规表达式匹配的部分,第四个参数被认为是

一个变量,替换后的字符串存入这个变量中。例如:

regsub there "They live there lives " their x 

puts $x 

显示结果为:They live their lives

【sting】

语法:string subcmd arg [arg...]

string命令具有强大的操作字符串的功能,并通过其中的subcmd来区别具体将要执行的string操作。他们中的一些如下:

string length string

string range string first last

string index string charIndex

string tolower string [first] [last]

string replace string first last [newstring]

string equal [-nocase] [-length int] string1 string2

string match [-nocase] pattern string  如果pattern 匹配string,那么返回1,否则返回0.

string compare [-nocase] [-length int] string1 string2   如果有 -length 参数,那么只比较前 int 个字符

string first string1 string2 [startindex]

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

匹配的第一个字母所在的位置(0-based)。如果没有找到,那么返回-1。

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

给出chars,那么将删除掉spaces、tabs、newlines、carriage returns这些字符.

2、list结构及其操作

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

任意字符串,也可以是list。Tcl提供了很多基本命令对list进行操作:

【list】

语法:list [ value value...]

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

【concat】

语法:concat list [list...]

这个命令把多个list合成一个list.

【llength】

语法:llength list

返回list的元素个数。

【lindex】

语法:lindex list index

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

【lrange】

语法:lrange list first last 

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

【linsert】

语法:linsert list index value [value...?]

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

【lappend】

语法:lappend varname value [value...?]

把每个value的值作为一个元素附加到变量varname后面,并返回变量的新值,如果varname不存在,就生成这个变

量。

【lreplace】

语法:lreplace list first last [value value ...]

 

返回一个新串,新串是把list的第firs (0-based)t到第last 个(0-based)元素用所有的value参数替换得到的。如果没有

value参数,就表示删除第first到第last个元素。

【lsort】

语法:lsort [options?] list 

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

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

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

(1)不考虑大小写 

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

【lsearch】

语法:lsearch [-option]  list pattern 

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

种模式匹配的技术,缺省时使用-glob匹配。

【join】

语法:join list [joinString]

这个命令把list的所有元素合并到一个字符串中,中间以joinString分开。缺省的joinString是空格。

【split】

语法:split string [splitChars]

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

string被按字符分开。如果splitChars没有给出,以空格为分隔符。

3、file结构及其操作

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

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

3.1基本文件I/O

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

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

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

proc tgrep { pattern filename} { 

set f [open $filename r] 

while { [gets $f line ] } { 

if {[regexp $pattern $line]} { 

puts stdout $line 

close $f 

}

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

时,就可以使用这个文件标识符。TCL有三个特定的文件标识: stdin,stdout和stderr ,分别对应标准输入、标准输出和

错误通道,任何时候你都可以使用这三个文件标识。

3.2随即文件访问

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

命令写数据是接着上次puts写的位置接着写。TCL提供了seek,tell和eof等命令使用户可以非连续访问文件。

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

这取决于打开文件时使用的访问模式。每次读写后访问位置按访问的字节数后移相应的位数。

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

end,默认是start。

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

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

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

【grob】

语法:glob [switches] pattern [pattern ...]

glob命令的模式采用string match命令的匹配规则,返回匹配这个(些)模式的所有文件的列表。如:

glob *.c *.h

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

【file】

语法:file subcmd arg [arg...]

file 通过众多的subcmd子操作提供强大的文件操作和访问能力。他们中的一些如下:

file exists name

file extension name

file mkdir dir [dir ...]

file copy [-force] [--] source target

file rename [-force] [--] source target

file delete [-force] [--] pathname [pathname ... ]

三、Tcl表达式

(1)操作数

Tcl表达式的操作数通常是整数或实数。整数一般是十进制的, 但如果整数前两个字符是0x则这个整数被看作是十六进制的。

Tcl的实数的写法与ANSI C中完全一样。

 

(2)运算符

Tcl中的运算符的语法形式和用法跟ANSI C中完全一致。

另外,Tcl中提供了大量的常用数学函数来增强自身的数学运算和处理能力,这些函数的形式和用法大都与ANSI C中的数学函数

完全一致。

 

 

四、Tcl流程控制

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

(1)条件语句

if { $x>0 } {

..... 

}elseif { $x==1 } { 

..... 

}elseif { $x==2 } { 

.... 

}else { 

..... 

}

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

从而导致错误的结果。在Tcl的其他流程控制命令的书写中也要注意这个问题。书写中还要注意的一个问题是if 和{之间应该有一个

空格,否则TCL解释器会把'if{'作为一个整体当作一个命令名,从而导致错误。

 

switch [option] $x { 

a - 

b {incr t1} 

c {incr t2} 

default {incr t3} 

}

其中可选参数option,表示进行匹配的方式。TCL支持三种匹配方式:-exact方式,-glob方式,-regexp方式,缺省情况表示-glob

方式。-exact方式表示的是精确匹配,-glob方式的匹配方式和string match 命令的匹配方式相同,-regexp方式是正规表达式匹配方式。

条件中a的后面跟一个'-'表示使用和下一个模式相同的脚本,default表示匹配任意值。一旦switch命令 找到一个模式匹配,就执行相

应的脚本,并返回脚本的值作为switch命令的返回值。

 

(2)循环语句

for init test reinit body 

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

的脚本,第四个参数body也是脚本。如:

 

set b " " 

for {set i [expr [llength $a] -1]} {$i>=0} {incr i -1} {

lappend b [lindex $a $i] 

}

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

set b " " 

set i [expr [llength $a] -1] 

while { $i>=0}{ 

lappend b [lindex $a $i] 

incr i -1 

}

 

foreach命令的两种形式:

1、foreach varName list body 

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

每次取得链表的一个元素,都会执行循环体一次。如:

set b " " 

foreach i $a{

set b [linsert $b 0 $i]

}

2、foreach varlist1 list1 varlist2 list2 ... body

本语句是上一个的增强形式:可以有多个变量、列表对参与条件判断

 

 

五、Tcl过程

TCL支持过程的定义和调用,在TCL中,过程可以看作是用TCL脚本实现的命令,效果与TCL的固有命令相似。我们可以在任何

时候使用proc命令定义自己的过程,TCL中的过程类似于C中的函数。

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

proc add {x y } {expr $x+$y}

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

TCL脚本,代表过程体。 proc生成一个新的命令,可以象固有命令一样调用:

add 1 2 3

在定义过程时,你可以利用return命令在任何地方返回你想要的值。return命令迅速中断过程,并把它的参数作为过程的结果。

 

(1)局部变量和全局变量

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

程之外定义的变量我们称之为全局变量。TCL中局部变量和全局变量可以同名,两者的作用域的交集为空:局部变量的作用域是

它所在的过程的内部;全局变量的作用域则不包括任何过程的内部。这一点和C语言有很大的不同。

 

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

(2)缺省参数和可变参数

可以定义具有缺省参数值的过程,我们可以为过程的部分或全部参数提供缺省值,如果调用过程时未提供那些参数的值,那

么过程会自动使用缺省值赋给相应的参数。和C\C++中具有缺省参数值的函数一样,有缺省值的参数只能位于参数列表的后部,即

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

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

以前的参数象普通参数一样处理,但任何附加的参数都需要在过程体中作特殊处理,过程的局部变量args将会被设置为一个列表,

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

 

(3)引用:upvar

命令语法:upvar [level] otherVar myVar otherVar myVar ...

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

upvar命令的第一个参数otherVar是我们希望以引用方式访问的参数的名字,第二个参数myVar 是这个过程中的局部变量的名字,

一旦使用了upvar 命令把otherVar 和myVar 绑定,那么在过程中对局部变量myVar 的读写就相当于对这个过程的调用者中otherVar 所代

表的局部变量的读写。下面是一个例子:

proc temp { arg } { upvar $arg b set b [expr $b+2] } 

proc myexp { var } { set a 4 temp a return [expr $var+$a] }

则: myexp 7 13

这个例子中,upvar 把$arg(实际上是过程myexp中的变量a)和过程temp中的变量b绑定,对b的读写就相当于对a的读写。

 

upvar命令语法中的level参数表示:调用upvar命令的过程相对于我们希望引用的变量myVar在调用栈中相对位置。例如:

upvar 2 other x

这个命令使得当前过程的调用者的调用者中的变量other,可以在当前过程中利用x访问。缺省情况下,level的值为1,即当前过

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

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

upvar #0 other x

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