tcl入門經典

Tcl TCL語法
■腳本、命令和單詞符號
一個TCL腳本可以包含一個或多個命令。命令之間必須用換行符或分號隔開,下面的兩個腳本都是合法的:
set a 1 set b 2

set a 1;set b 2
TCL的每一個命令包含一個或幾個單詞,第一個單詞代表命令名,另外的單詞則是這個命令的參數,單詞之間必須用空格或TAB鍵隔開。 TCL解釋器對一個命令的求值過程分爲兩部分:分析和執行。在分析階段,TCL 解釋器運用規則把命令分成一個個獨立的單詞,同時進行必要的置換(substitution); 在執行階段,TCL 解釋器會把第一個單詞當作命令名,並查看這個命令是否有定義,如果有定義就激活這個命令對應的C/C++過程,並把所有的單詞作爲參數傳遞給該命令過程,讓命令過程進行處理。
6 3/25/2006 10:11:08 PM
■置換(substitution)
注:在下面的所有章節的例子中,'%'爲TCL的命令提示符,輸入命令回車後,TCL會在接着的一行輸出命令執行結果。'//'後面是我自己加上的說明,不是例子的一部分。
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 y $x+100 //y的值是10+100,這裏x被置換成它的值10
10+100
這時,y的值還不是我們想要的值110,而是10+100,因爲TCL解釋器把10+100看成是一個字符串而不是表達式,y要想得到值110,還必須用命令置換,使得TCL會把10+100看成一個表達式並求值。
■命令置換(command substitution)
命令置換是由[]括起來的TCL命令及其參數,命令置換會導致某一個命令的所有或部分單詞被另一個命令的結果所代替。例如:
%set y [expr $x+100]
110
7 3/25/2006 10:11:08 PM
y的值是110,這裏當TCL解釋器遇到字符'['時,它就會把隨後的expr作爲一個命令名,從而激活與expr對應的C/C++過程,並把'expr'和變量置換後得到的'10+110'傳遞給該命令過程進行處理。
如果在上例中我們去掉[],那麼TCL會報錯。因爲在正常情況下,TCL解釋器只把命令行中的第一個單詞作爲看作命令,其他的單詞都作爲普通字符串處理,看作是命令的參數。
注意,[]中必須是一個合法的TCL腳本,長度不限。[]中腳本的值爲最後一個命令的返回值,例如:
%set y [expr $x+100;set b 300] //y的值爲300,因爲set b 300的返回值爲300 300
有了命令置換,實際上就表示命令之間是可以嵌套的,即一個命令的結果可以作爲別的命令的參數。
■反斜槓置換(backslash substitution)
TCL語言中的反斜槓置換類似於C語言中反斜槓的用法,主要用於在單詞符號中插入諸如換行符、空格、[、$等被TCL解釋器當作特殊符號對待的字符。例如:
set msg multiple/ space //msg的值爲multiple space。
如果沒有'/'的話,TCL會報錯,因爲解釋器會把這裏最後兩個單詞之間的空格認爲是分隔符,於是發現set命令有多於兩個參數,從而報錯。加入了'/'後,空格不被當作分隔符,'multiple space'被認爲是一個單詞(word)。又例如:
%set msg money/ /$3333/ /nArray/ a/[2] //這個命令的執行結果爲:money $3333 Array a[2] 這裏的$不再被當作變量置換符。 TCL支持以下的反斜槓置換: Backslash Sequence Replaced By /a Audible alert (0x7) /b Backspace (0x8) /f Form feed (0xc) /n Newline (0xa) /r Carriage return (0xd) /t Tab (0x9) /v Vertical tab (0xb) /ddd Octal value given by ddd (one, two, or three d's) /xhh Hex value given by hh
8 3/25/2006 10:11:08 PM
(any number of h's) / newline space A single space character.
例如:
%set a /x48 //對應 /xhh H //十六進制的48正好是72,對應H % set a /110 //對應 /ddd H //八進制的110正好是72,對應H %set a [expr / // 對應/newline space,一個命令可以用/newline轉到下一行繼續 2+3] 5
■雙引號和花括號
除了使用反斜槓外,TCL提供另外兩種方法來使得解釋器把分隔符和置換符等特殊字符當作普通字符,而不作特殊處理,這就要使用雙引號和花括號({})。
TCL解釋器對雙引號中的各種分隔符將不作處理,但是對換行符 及$和[]兩種置換符會照常處理。例如:
%set x 100 100 %set y "$x ddd" 100 ddd
而在花括號中,所有特殊字符都將成爲普通字符,失去其特殊意義,TCL解釋器不會對其作特殊處理。
%set y {/n$x [expr 10+100]} /n$x [expr 10+100]
9 3/25/2006 10:11:08 PM
■註釋
TCL中的註釋符是'#','#'和直到所在行結尾的所有字符都被TCL看作註釋,TCL解釋器對註釋將不作任何處理。不過,要注意的是,'#'必須出現在TCL解釋器期望命令的第一個字符出現的地方,才被當作註釋。
例如:
%#This is a comment %set a 100 # Not a comment wrong # args: should be "set varName ?newValue?" %set b 101 ; # this is a comment 101
第二行中'#'就不被當作註釋符,因爲它出現在命令的中間,TCL解釋器把它和後面的字符當作命令的參數處理,從而導致錯誤。而第四行的'#'就被作爲註釋,因爲前一個命令已經用一個分號結束,TCL解釋器期望下一個命令接着出現。現在在這個位置出現'#',隨後的字符就被當作註釋了。
1 0 3/25/2006 10:11:08 PM
變量
■簡單變量
一個TCL的簡單變量包含兩個部分:名字和值。名字和值都可以是任意字符串。例如一個名爲 “1323 7&*: hdgg"的變量在TCL中都是合法的。不過爲了更好的使用置換(substitution),變量名最好按C/C++語言中標識符的命名規則命名。 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中的set命令能生成一個變量、也能讀取或改變一個變量的值。例如:
% set a {kdfj kjdf} kdfj kjdf
如果變量a還沒有定義,這個命令將生成 變量a,並將其值置爲kdfj kjdf,若a已定義,就簡單的把a的值置爲kdfj kjdf。
%set a kdfj kjdf
這個只有一個參數的set命令讀取a的當前值kdfj kjdf。
1 1 3/25/2006 10:11:08 PM
■數組
數組是一些元素的集合。TCL的數組和普通計算機語言中的數組有很大的區別。在TCL中,不能單獨聲明一個數組,數組只能和數組元素一起聲明。數組中,數組元素的名字包含兩部分:數組名和數組中元素的名字,TCL中數組元素的名字(下標〕可以爲任何字符串。例如:
set day(monday) 1 set day(tuesday) 2
第一個命令生成一個名爲day的數組,同時在數組中生成一個名爲monday的數組元素,並把值置爲1,第二個命令生成一個名爲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)的值。
TCL不能支持複雜的數據類型,這是一個很大的缺憾,也是TCL受指責很多的方面。但是TCL的一個擴展ITCL填補了這個缺憾
1 2 3/25/2006 10:11:08 PM
■相關命令
■set
這個命令在3.1已有詳細介紹。
■unset
這個命令從解釋器中刪除變量,它後面可以有任意多個參數,每個參數是一個變量名,可以是簡單變量,也可以是數組或數組元素。例如:
% unset a b day(monday)
上面的語句中刪除了變量a、b和數組元素day(monday),但是數組day並沒有刪除,其他元素還存在,要刪除整個數組,只需給出數組的名字。例如:
%puts $day(monday) can't read "day(monday)": no such element in array % puts $day(tuesday) 2 %unset day % puts $day(tuesday) can't read "day(tuesday)": no such variable
■append和incr
這兩個命令提供了改變變量的值的簡單手段。
append命令把文本加到一個變量的後面,例如:
% set txt hello hello % append txt "! How are you" hello! How are you
incr命令把一個變量值加上一個整數。incr要求變量原來的值和新加的值都必須是整數。
%set b a a % incr b expected integer but got "a" %set b 2
1 3 3/25/2006 10:11:08 PM
2 %incr b 3 5
1 4 3/25/2006 10:11:08 PM
表達式
■操作數
TCL表達式的操作數通常是整數或實數。整數一般是十進制的, 但如果整數的第一個字符是0(zero),那麼TCL將把這個整數看作八進制的,如果前兩個字符是0x則這個整數被看作是十六進制的。TCL的實數的寫法與ANSI C中完全一樣。如:
2.1 7.9e+12 6e4 3.
1 5 3/25/2006 10:11:08 PM
■運算符和優先級
下面的表格中列出了TCL中用到的運算符,它們的語法形式和用法跟ANSI C中很相似。這裏就不一一介紹。下表中的運算符是按優先級從高到低往下排列的。同一格中的運算符優先級相同。
語法形式
結果
操作數類型
-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<<b
a>>b
左移位
右移位
int
int
a<b
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
1 6 3/25/2006 10:11:08 PM
■數學函數
TCL支持常用的數學函數,表達式中數學函數的寫法類似於C/C++語言的寫法,數學函數的參數可以是任意表達式,多個參數之間用逗號隔開。例如:
%set x 2
2
% expr 2* sin($x<3)
1.68294196962
其中expr是TCL的一個命令,語法爲: expr arg ?arg ...?
兩個 ?之間的參數表示可省,後面介紹命令時對於可省參數都使用這種表示形式。 expr可以有一個或多個參數,它把所有的參數組合到一起,作爲一個表達式,然後求值:
%expr 1+2*3
7
%expr 1 +2 *3
7
需要注意的壞閌牽Ш    ⒉皇敲 睿 輝詒澩鍤街諧魷植龐幸庖濉?
TCL中支持的數學函數如下
abs( x) Absolute value of x.
acos( x) Arc cosine of x, in the range 0 to p.
asin( x) Arc sine of x, in the range -p/2 to p/2.
atan( x) Arc tangent of x, in the range -p/2 to p/2.
atan2( x, y) Arc tangent of x/ y, in the range -p/2 to p/2.
ceil( x) Smallest integer not less than x.
cos( x) Cosine of x ( x in radians).
cosh( x) Hyperbolic cosine of x.
1 7 3/25/2006 10:11:08 PM
double( i) Real value equal to integer i.
exp( x) e raised to the power x.
floor( x) Largest integer not greater than x.
fmod( x, y) Floating-point remainder of x divided by y.
hypot( x, y) Square root of ( x 2 + y 2 ).
int( x) Integer value produced by truncating x.
log( x) Natural logarithm of x.
log10( x) Base 10 logarithm of x.
pow( x, y) x raised to the power y.
round( x) Integer value produced by rounding x.
sin( x) Sine of x ( x in radians).
sinh( x) Hyperbolic sine of x.
sqrt( x) Square root of x.
tan( x) Tangent of x ( x in radians).
tanh( x) Hyperbolic tangent of x.
TCL中有很多命令都以表達式作爲參數。最典型的是expr命令,另外if、while、for等循環控制命令的循環控制中也都使用表達式作爲參數。
1 8 3/25/2006 10:11:08 PM
List
■list命令
list這個概念在TCL中是用來表示集合的。TCL中list是由一堆元素組成的有序集合,list可以嵌套定義,list每個元素可以是任意字符串,也可以是list。下面都是TCL中的合法的list:
{} //空list {a b c d} {a {b c} d} //list可以嵌套
list是TCL中比較重要的一種數據結構,對於編寫複雜的腳本有很大的幫助,TCL提供了很多基本命令對list進行操作,下面一一介紹:
語法: list ? value value...? 這個命令生成一個list,list的元素就是所有的value。例:
% list 1 2 {3 4} 1 2 {3 4}
1 9 3/25/2006 10:11:08 PM
■concat命令
語法:concat list ?list...?
這個命令把多個list合成一個list,每個list變成新list的一個元素。
2 0 3/25/2006 10:11:08 PM
■lindex命令
語法:lindex list index 返回list的第index個(0-based)元素。例:
% lindex {1 2 {3 4}} 2 3 4
2 1 3/25/2006 10:11:08 PM
■llength命令
語法:llength list 返回list的元素個數。例
% llength {1 2 {3 4}} 3
2 2 3/25/2006 10:11:08 PM
■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}
2 3 3/25/2006 10:11:08 PM
■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
2 4 3/25/2006 10:11:08 PM
■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
2 5 3/25/2006 10:11:08 PM
■lappend命令
語法:lappend varname value ?value...? 把每個value的值作爲一個元素附加到變量varname後面,並返回變量的新值,如果varname不存在,就生成這個變量。例:
% lappend a 1 2 3 1 2 3 % set a 1 2 3
2 6 3/25/2006 10:11:08 PM
■lsearch 命令
語法:lsearch ?-exact? ?-glob? ?-regexp? list pattern 返回list中第一個匹配模式pattern的元素的索引,如果找不到匹配就返回-1。-exact、-glob、 -regexp是三種模式匹配的技術。-exact表示精確匹配;-glob的匹配方式和string match命令的匹配方式相同,將在後面第八節介紹string命令時介紹;-regexp表示正規表達式匹配,將在第八節介紹regexp命令時介紹。缺省時使用-glob匹配。例:
% set a { how are you } how are you % lsearch $a y* 2 % lsearch $a y? -1
2 7 3/25/2006 10:11:08 PM
■lsort命令
語法: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字符比較)
-command command TCL自動利用command 命令把每兩個元素一一比較,然後給出排序結果。
2 8 3/25/2006 10:11:08 PM
■split命令
語法:split string ?splitChars? 把字符串string按分隔符splitChars分成一個個單詞,返回由這些單詞組成的串。如果splitChars 是一個空字符{},string被按字符分開。如果splitChars沒有給出,以空格爲分隔符。例:
% split "how.are.you" . how are you % split "how are you" how are you % split "how are you" {} h o w { } a r e { } y o u
2 9 3/25/2006 10:11:08 PM
■join命令
語法:join list ?joinString? join命令是命令的逆。這個命令把list的所有元素合併到一個字符串中,中間以joinString分開。缺省的joinString是空格。例:
% join {h o w { } a r e { } y o u} {} how are you % join {how are you} . how.are.you
3 0 3/25/2006 10:11:08 PM
控制流
■if命令
TCL中的控制流和C語言類似,包括if、while、for、foreach、switch、break、continue等命令。
語法: 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{ ..... }
注意,上例中'{'一定要寫在上一行,因爲如果不這樣,TCL 解釋器會認爲if命令在換行符處已結束,下一行會被當成新的命令,從而導致錯誤的結果。在下面的循環命令的書寫中也要注意這個問題。書寫中還要注意的一個問題是if 和{之間應該有一個空格,否則TCL解釋器會把'if{'作爲一個整體當作一個命令名,從而導致錯誤
3 1 3/25/2006 10:11:08 PM
■循環命令:while 、for、 foreach
循環命令包括while、for、foreach等。
■while命令
語法爲: while test body 參數test是一個表達式,body是一個腳本,如果表達式的值非0,就運行腳本,直到表達式爲0才停止循環,此時while命令中斷並返回一個空字符串。
例如: 假設變量 a 是一個鏈表,下面的腳本把a 的值複製到b: set b " " set i [expr [llength $a] -1] while { $i>=0}{ lappend b [lindex $a $i] incr i -1 }
■for命令
語法爲: for init test reinit body 參數init是一個初始化腳本,第二個參數test是一個表達式,用來決定循環什麼時候中斷,第三個參數reinit是一個重新初始化的腳本,第四個參數body也是腳本, 硌 誹濉O呂 肷俠 饔孟嗤? 
set b " " for {set i [expr [llength $a] -1]} {$i>=0} {incr i -1} { lappend b [lindex $a $i] }
■foreach命令
這個命令有兩種語法形式
1、 foreach varName list body 第一個參數varName是一個變量,第二個參數list 是一個表(有序集合),第三個參數body是循環體。每次取得鏈表的一個元素,都會執行循環體一次。 下例與上例作用相同:
set b " " foreach i $a{
3 2 3/25/2006 10:11:08 PM
set b [linsert $b 0 $i] }
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"。
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只是結束本次循環。
■switch 命令
和C語言中switch語句一樣,TCL中的switch命令也可以由if命令實現。只是書寫起來較爲煩瑣。 switch命令的語法爲: switch ? options? string { pattern body ? pattern body ...?}
第一個是可選參數options,表示進行匹配的方式。TCL支持三種匹配方式:-exact方式,-glob方式,-regexp方式,缺省情況表示-glob方式。-exact方式表示的是精確匹配,-glob方式的匹配方式和string match 命令的匹配方式相同(第八節介紹),-regexp方式是正規表達式的
3 3 3/25/2006 10:11:08 PM
匹配方式(第八節介紹)。第二個參數string 是要被用來作測試的值,第三個參數是括起來的一個或多個元素對,例:
switch $x { a - b {incr t1} c {incr t2} default {incr t3} }
其中a的後面跟一個'-'表示使用和下一個模式相同的腳本。default表示匹配任意值。一旦switch命令 找到一個模式匹配,就執行相應的腳本,並返回腳本的值,作爲switch命令的返回值。
3 4 3/25/2006 10:11:08 PM
■eval命令
eval命令是一個用來構造和執行TCL腳本的命令,其語法爲: eval arg ?arg ...?
它可以接收一個或多個參數,然後把所有的參數以空格隔開組合到一起成爲一個腳本,然後對這個腳本進行求值。例如:
%eval set a 2 ;set b 4 4
3 5 3/25/2006 10:11:08 PM
■source命令
source命令讀一個文件並把這個文件的內容作爲一個腳本進行求值。例如:
source e:/tcl&c/hello.tcl
注意路徑的描述應該和UNIX相同,使用'/'而不是'/'。
3 6 3/25/2006 10:11:08 PM
過程(procedure)
■過程定義和返回值
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命令迅速中斷過程,並把它的參數作爲過程的結果。例如:
% proc abs {x} { if {$x >= 0} { return $x } return [expr -$x] }
過程的返回值是過程體中最後執行的那條命令的返回值。
3 7 3/25/2006 10:11:08 PM
■局部變量和全局變量
對於在過程中定義的變量,因爲它們只能在過程中被訪問,並且當過程退出時會被自動刪除,所以稱爲局部變量;在所有過程之外定義的變量我們稱之爲全局變量。TCL中,局部變量和全局變量可以同名,兩者的作用域的交集爲空:局部變量的作用域是它所在的過程的內部;全局變量的作用域則不包括所有過程的內部。這一點和C語言有很大的不同.
如果我們想在過程內部引用一個全局變量的值,可以使用global命令。例如:
% set a 4 4 % proc sample { x } { global a incr a return [expr $a+$x] } % sample 3 8 %set a 5
全局變量a在過程中被訪問。在過程中對a的改變會直接反映到全局上。如果去掉語句global a,TCL會出錯,因爲它不認識變量a。
3 8 3/25/2006 10:11:08 PM
■缺省參數和可變個數參數
TCL還提供三種特殊的參數形式: 首先,你可以定義一個沒有參數的過程,例如:
proc add {} { expr 2+3}
其次,可以定義具有缺省參數值的過程,我們可以爲過程的部分或全部參數提供缺省值,如果調用過程時未提供那些參數的值,那麼過程會自動使用缺省值賦給相應的參數。和C/C++中具有缺省參數值的函數一樣,有缺省值的參數只能位於參數列表的後部,即在第一個具有缺省值的參數後面的所有參數,都只能是具有缺省值的參數。
例如: proc add {val1 {val2 2} {val3 3}}{ expr $val1+$val2+$val3 }
則: add 1 //值爲6 add 2 20 //值爲25 add 4 5 6 //值爲15
另外,TCL的過程定義還支持可變個數的參數,如果過程的最後一個參數是args, 那麼就表示這個過程支持可變個數的參 饔謾5饔檬?位於args以前的參數象普通參數一樣處理,但任何附加的參數都需要在過程體中作特殊處理,過程的局部變量args將會被設置爲一個列表,其元素就是所有附加的變量。如果沒有附加的變量,args就設置成一個空串,下面是一個例子:
proc add { val1 args } { set sum $val1 foreach i $args { incr sum $i } return $sum }
則: add 2 //值爲2 add 2 3 4 5 6 //值爲20
3 9 3/25/2006 10:11:08 PM
■引用: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。
4 0 3/25/2006 10:11:08 PM
字符串操作
■format命令
因爲TCL把所有的輸入都當作字符串看待,所以TCL提供了較強的字符串操作功能,TCL中與字符串操作有關的命令有:string、format、regexp、regsub、scan等。
format命令
語法:format formatstring ?vlue value...? format命令類似於ANSIC中的sprintf函數和MFC中CString類提供的Format成員函數。它按formatstring提供的格式,把各個value的值組合到formatstring中形成一個新字符串,並返回。例如:
%set name john John %set age 20 20 %set msg [format "%s is %d years old" $name $age] john is 20 years old
4 1 3/25/2006 10:11:08 PM
■scan命令
語法:scan string format varName ?varName ...? scan命令可以認爲是format命令的逆,其功能類似於ANSI C中的sscanf函數。它按format提供的格式分析string字符串,然後把結果存到變量varName中,注意除了空格和TAB鍵之外,string 和format中的字符和'%'必須匹配。例如:
% scan "some 26 34" "some %d %d" a b 2 % set a 26 % set b 34 % scan "12.34.56.78" "%d.%d.%d.%d" c d e f 4 % puts [format "the value of c is %d,d is %d,e is %d ,f is %d" $c $d $e $f] the value of c is 12,d is 34,e is 56 ,f is 78
scan命令的返回值是匹配的變量個數。而且,我們發現,如果變量varName不存在的話,TCL會自動聲明該變量。
4 2 3/25/2006 10:11:08 PM
■regexp命令
語法:regexp ?switchs? ?--? exp string ?matchVar?/ ?subMatchVar subMatchVar...? 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中的一項
下面的一個例子是從《Tcl and Tk ToolKit》中摘下來的,下面進行說明:
^((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
4 3 3/25/2006 10:11:08 PM
% 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]+)} " there is 100 apples" total num word 1 % puts " $total ,$num,$word" 100 apples ,100,apples
regexp可以設置一些開關(switchs〕,來控制匹配結果:
開關
意義
-nocase
匹配時不考慮大小寫
-indices
改變各個變量的值,這使各個變量的值變成了對應的匹配子串在整個字符串中所處位置的索引。例如:
% regexp -indices { ([0-9]+) *([a-z]+)} " there is 100 apples" total num word 1 % puts " $total ,$num,$word" 9 20 ,10 12,15 20
正好子串“ 100 apples”的序號是9-20,"100"的序號是10-12,"apples"的序號是15-20
-about
返回正則表達式本身的信息,而不是對緩衝區的解析。返回的是一個list,第一個元素是子表達式的個數,第二個元素開始存放子表達式的信息
-expanded
啓用擴展的規則,將空格和註釋忽略掉,相當於使用內嵌語法(?x)
-line
啓用行敏感匹配。正常情況下^和$只能匹配緩衝區起始和末尾,對於緩衝區內部新的行是不能匹配的,通過這個開關可以使緩衝區內部新的行也可以被匹配。它相當於同時使用-linestop和-lineanchor 開關,或者使用內嵌語法(?n)
-linestop
啓動行結束敏感開關。使^可以匹配緩衝區內部的新行。相當於內嵌語法(?p)
-lineanchor
改變^和$的匹配行爲,使可以匹配緩衝區內部的新行。相當於內嵌語法(?w)
-all
進最大可能的匹配
-inline
Causes the command to return, as a list, the data that would
4 4 3/25/2006 10:11:08 PM
otherwise be placed in match variables. When using -inline, match variables may not be specified. If used with -all, the list will be concatenated at each iteration, such that a flat list is always returned. For each match iteration, the command will append the overall match data, plus one element for each subexpression in the regular expression. Examples are: regexp -inline -- {/w(/w)} " inlined " => {in n} regexp -all -inline -- {/w(/w)} " inlined " => {in n li i ne e}
-start index
強制從偏移爲index開始的位置進行匹配。使用這個開關之後,^將不能匹配行起始位置,/A將匹配字符串的index偏移位置。如果使用了-indices開關,則indices表示絕對位置,index表示輸入字符的相對位置。
--
表示這後面再沒有開關(switchs〕了,即使後面有以'-'開頭的參數也被當作正規表達式的一部分。
【TCL正則表達式規則詳細說明】
◆DESCRIPTION(描述)
A regular expression describes strings of characters. It's a pattern that matches certain strings and doesn't match others. ◆DIFFERENT FLAVORS OF REs(和標準正則表達式的區別) Regular expressions, as defined by POSIX, come in two flavors: extended REs and basic REs. EREs are roughly those of the traditional egrep, while BREs are roughly those of the traditional ed. This implementation adds a third flavor, advanced REs, basically EREs with some significant extensions. This manual page primarily describes AREs. BREs mostly exist for backward compatibility in some old programs; they will be discussed at the end. POSIX EREs are almost an exact subset of AREs. Features of AREs that are not present in EREs will be indicated.
4 5 3/25/2006 10:11:08 PM
◆REGULAR EXPRESSION SYNTAX(語法) Tcl regular expressions are implemented using the package written by Henry Spencer, based on the 1003.2 spec and some (not quite all) of the Perl5 extensions (thanks, Henry!). Much of the description of regular expressions below is copied verbatim from his manual entry. An ARE is one or more branches, separated by `|', matching anything that matches any of the branches. A branch is zero or more constraints or quantified atoms, concatenated. It matches a match for the first, followed by a match for the second, etc; an empty branch matches the empty string. A quantified atom is an atom possibly followed by a single quantifier. Without a quantifier, it matches a match for the atom. The quantifiers, and what a so-quantified atom matches, are:
字符
意義
*
a sequence of 0 or more matches of the atom
+
a sequence of 1 or more matches of the atom
?
a sequence of 0 or 1 matches of the atom
{m}
a sequence of exactly m matches of the atom
{m,}
a sequence of m or more matches of the atom
{m,n}
a sequence of m through n (inclusive) matches of the atom; m may not exceed n
*? +? ?? {m}? {m,}? {m,n}?
non-greedy quantifiers, which match the same possibilities, but prefer the smallest number rather than the largest number of matches (see MATCHING)
The forms using { and } are known as bounds. The numbers m and n are unsigned decimal integers with permissible values from 0 to 255 inclusive. An atom is one of:
字符
意義
(re)
(where re is any regular expression) matches a match for re, with the match noted for possible reporting
(?:re)
as previous, but does no reporting
()
matches an empty string, noted for possible reporting
(?:)
matches an empty string, without reporting
[chars]
a bracket expression, matching any one of the chars (see BRACKET EXPRESSIONS for more detail)
4 6 3/25/2006 10:11:08 PM
.
matches any single character
/k
where k is a non-alphanumeric character) matches that character taken as an ordinary character, e.g. // matches a backslash character
/c
where c is alphanumeric (possibly followed by other characters), an escape (AREs only), see ESCAPES below
{
when followed by a character other than a digit, matches the left-brace character `{'; when followed by a digit, it is the beginning of a bound (see above)
x
where x is a single character with no other significance, matches that character.
A constraint matches an empty string when specific conditions are met. A constraint may not be followed by a quantifier. The simple constraints are as follows; some more constraints are described later, under ESCAPES.
字符
意義
^
matches at the beginning of a line
$
matches at the end of a line
(?=re)
positive lookahead (AREs only), matches at any point where a substring matching re begins
(?!re)
negative lookahead (AREs only), matches at any point where no substring matching re begins
The lookahead constraints may not contain back references (see later), and all parentheses within them are considered non-capturing. An RE may not end with `/'. ◆BRACKET EXPRESSIONS(預定義表達式) A bracket expression is a list of characters enclosed in `[]'. It normally matches any single character from the list (but see below). If the list begins with `^', it matches any single character (but see below) not from the rest of the list. If two characters in the list are separated by `-', this is shorthand for the full range of characters between those two (inclusive) in the collating sequence, e.g. [0-9] in ASCII matches any decimal digit. Two ranges may not share an endpoint, so e.g. a-c-e is illegal. Ranges are very collating-sequence-dependent, and portable programs should avoid relying on them. To include a literal ] or - in the list, the simplest method is to enclose it in [. and .] to make it a collating element (see below). Alternatively, make it the first character (following a possible `^'), or (AREs only) precede it with `/'. Alternatively, for `-',
4 7 3/25/2006 10:11:08 PM
make it the last character, or the second endpoint of a range. To use a literal - as the first endpoint of a range, make it a collating element or (AREs only) precede it with `/'. With the exception of these, some combinations using [ (see next paragraphs), and escapes, all other special characters lose their special significance within a bracket expression. Within a bracket expression, a collating element (a character, a multi-character sequence that collates as if it were a single character, or a collating-sequence name for either) enclosed in [. and .] stands for the sequence of characters of that collating element. The sequence is a single element of the bracket expression's list. A bracket expression in a locale that has multi-character collating elements can thus match more than one character. So (insidiously), a bracket expression that starts with ^ can match multi-character collating elements even if none of them appear in the bracket expression! (Note: Tcl currently has no multi-character collating elements. This information is only for illustration.) For example, assume the collating sequence includes a ch multi-character collating element. Then the RE [[.ch.]]*c (zero or more ch's followed by c) matches the first five characters of `chchcc'. Also, the RE [^c]b matches all of `chb' (because [^c] matches the multi-character ch). Within a bracket expression, a collating element enclosed in [= and =] is an equivalence class, standing for the sequences of characters of all collating elements equivalent to that one, including itself. (If there are no other equivalent collating elements, the treatment is as if the enclosing delimiters were `[.' and `.]'.) For example, if o and ?are the members of an equivalence class, then `[[=o=]]', `[[=?]]', and `[o' are all synonymous. An equivalence class may not be an endpoint of a range. (Note: Tcl currently implements only the Unicode locale. It doesn't define any equivalence classes. The examples above are just illustrations.) Within a bracket expression, the name of a character class enclosed in [: and :] stands for the list of all characters (not all collating elements!) belonging to that class. Standard character classes are:
字符
意義
alpha
A letter.
upper
An upper-case letter.
lower
A lower-case letter.
digit
A decimal digit.
xdigit
A hexadecimal digit.
alnum
An alphanumeric (letter or digit).
print
An alphanumeric (same as alnum).
blank
A space or tab character.
4 8 3/25/2006 10:11:08 PM
space
A character producing white space in displayed text.
punct
A punctuation character.
graph
A character with a visible representation.
cntrl
A control character.
A locale may provide others. (Note that the current Tcl implementation has only one locale: the Unicode locale.) A character class may not be used as an endpoint of a range. There are two special cases of bracket expressions: the bracket expressions [[:<:]] and [[:>:]] are constraints, matching empty strings at the beginning and end of a word respectively. A word is defined as a sequence of word characters that is neither preceded nor followed by word characters. A word character is an alnum character or an underscore (_). These special bracket expressions are deprecated; users of AREs should use constraint escapes instead (see below). ◆ESCAPES(轉意字符) Escapes (AREs only), which begin with a / followed by an alphanumeric character, come in several varieties: character entry, class shorthands, constraint escapes, and back references. A / followed by an alphanumeric character but not constituting a valid escape is illegal in AREs. In EREs, there are no escapes: outside a bracket expression, a / followed by an alphanumeric character merely stands for that character as an ordinary character, and inside a bracket expression, / is an ordinary character. (The latter is the one actual incompatibility between EREs and AREs.) Character-entry escapes (AREs only) exist to make it easier to specify non-printing and otherwise inconvenient characters in REs:
字符
意義
/a
alert (bell) character, as in C
/b
backspace, as in C
/B
synonym for / to help reduce backslash doubling in some applications where there are multiple levels of backslash processing
/cX
(where X is any character) the character whose low-order 5 bits are the same as those of X, and whose other bits are all zero
/e
the character whose collating-sequence name is `ESC', or failing that, the character with octal value 033
/f
formfeed, as in C
/n
newline, as in C
/r
carriage return, as in C
4 9 3/25/2006 10:11:08 PM
/t
horizontal tab, as in C
/uwxyz
(where wxyz is exactly four hexadecimal digits) the Unicode character U+wxyz in the local byte ordering
/Ustuvwxyz
(where stuvwxyz is exactly eight hexadecimal digits) reserved for a somewhat-hypothetical Unicode extension to 32 bits
/v
vertical tab, as in C are all available.
/xhhh
(where hhh is any sequence of hexadecimal digits) the character whose hexadecimal value is 0xhhh (a single character no matter how many hexadecimal digits are used).
/0
the character whose value is 0
/xy
(where xy is exactly two octal digits, and is not a back reference (see below)) the character whose octal value is 0xy
/xyz
(where xyz is exactly three octal digits, and is not a back reference (see below)) the character whose octal value is 0xyz
Hexadecimal digits are `0'-`9', `a'-`f', and `A'-`F'. Octal digits are `0'-`7'. The character-entry escapes are always taken as ordinary characters. For example, /135 is ] in ASCII, but /135 does not terminate a bracket expression. Beware, however, that some applications (e.g., C compilers) interpret such sequences themselves before the regular-expression package gets to see them, which may require doubling (quadrupling, etc.) the `/'. Class-shorthand escapes (AREs only) provide shorthands for certain commonly-used character classes:
縮寫
代表的完整表達式
/d
[[:digit:]]
/s
[[:space:]]
/w
[[:alnum:]_] (note underscore)
/D
[^[:digit:]]
/S
[^[:space:]]
/W
[^[:alnum:]_] (note underscore)
Within bracket expressions, `/d', `/s', and `/w' lose their outer brackets, and `/D', `/S', and `/W' are illegal. (So, for example, [a-c/d] is equivalent to [a-c[:digit:]]. Also, [a-c/D], which is equivalent to [a-c^[:digit:]], is illegal.) A constraint escape (AREs only) is a constraint, matching the empty string if specific conditions are met, written as an escape:
字符
意義
5 0 3/25/2006 10:11:08 PM
/A
matches only at the beginning of the string (see MATCHING, below, for how this differs from `^')
/m
matches only at the beginning of a word
/M
matches only at the end of a word
/y
matches only at the beginning or end of a word
/Y
matches only at a point that is not the beginning or end of a word
/Z
matches only at the end of the string (see MATCHING, below, for how this differs from `$')
/m
(where m is a nonzero digit) a back reference, see below
/mnn
(where m is a nonzero digit, and nn is some more digits, and the decimal value mnn is not greater than the number of closing capturing parentheses seen so far) a back reference, see below
A word is defined as in the specification of [[:<:]] and [[:>:]] above. Constraint escapes are illegal within bracket expressions. A back reference (AREs only) matches the same string matched by the parenthesized subexpression specified by the number, so that (e.g.) ([bc])/1 matches bb or cc but not `bc'. The subexpression must entirely precede the back reference in the RE. Subexpressions are numbered in the order of their leading parentheses. Non-capturing parentheses do not define subexpressions. There is an inherent historical ambiguity between octal character-entry escapes and back references, which is resolved by heuristics, as hinted at above. A leading zero always indicates an octal escape. A single non-zero digit, not followed by another digit, is always taken as a back reference. A multi-digit sequence not starting with a zero is taken as a back reference if it comes after a suitable subexpression (i.e. the number is in the legal range for a back reference), and otherwise is taken as octal. ◆METASYNTAX(內嵌語法) In addition to the main syntax described above, there are some special forms and miscellaneous syntactic facilities available. Normally the flavor of RE being used is specified by application-dependent means. However, this can be overridden by a director. If an RE of any flavor begins with `***:', the rest of the RE is an ARE. If an RE of any flavor begins with `***=', the rest of the RE is taken to be a literal string, with all characters considered ordinary characters. An ARE may begin with embedded options: a sequence (?xyz) (where xyz is one or more alphabetic characters) specifies options affecting the rest of the RE. These
5 1 3/25/2006 10:11:08 PM
supplement, and can override, any options specified by the application. The available option letters are:
字符
意義
b
rest of RE is a BRE
c
case-sensitive matching (usual default)
e
rest of RE is an ERE
i
case-insensitive matching (see MATCHING, below)
m
historical synonym for n
n
newline-sensitive matching (see MATCHING, below)
p
partial newline-sensitive matching (see MATCHING, below)
q
rest of RE is a literal string, all ordinary characters
s
non-newline-sensitive matching (usual default)
t
tight syntax (usual default; see below)
w
inverse partial newline-sensitive matching (see MATCHING, below)
x
expanded syntax (see below)
Embedded options take effect at the ) terminating the sequence. They are available only at the start of an ARE, and may not be used later within it. In addition to the usual (tight) RE syntax, in which all characters are significant, there is an expanded syntax, available in all flavors of RE with the -expanded switch, or in AREs with the embedded x option. In the expanded syntax, white-space characters are ignored and all characters between a # and the following newline (or the end of the RE) are ignored, permitting paragraphing and commenting a complex RE. There are three exceptions to that basic rule: a white-space character or `#' preceded by `/' is retained white space or `#' within a bracket expression is retained white space and comments are illegal within multi-character symbols like the ARE `(?:' or the BRE `/(' Expanded-syntax white-space characters are blank, tab, newline, and any character that belongs to the space character class. Finally, in an ARE, outside bracket expressions, the sequence `(?#ttt)' (where ttt is any text not containing a `)') is a comment, completely ignored. Again, this is not allowed between the characters of multi-character symbols like `(?:'. Such comments are more a historical artifact than a useful facility, and their use is deprecated; use the expanded syntax instead.
5 2 3/25/2006 10:11:08 PM
None of these metasyntax extensions is available if the application (or an initial ***= director) has specified that the user's input be treated as a literal string rather than as an RE. ◆MATCHING(匹配) In the event that an RE could match more than one substring of a given string, the RE matches the one starting earliest in the string. If the RE could match more than one substring starting at that point, its choice is determined by its preference: either the longest substring, or the shortest. Most atoms, and all constraints, have no preference. A parenthesized RE has the same preference (possibly none) as the RE. A quantified atom with quantifier {m} or {m}? has the same preference (possibly none) as the atom itself. A quantified atom with other normal quantifiers (including {m,n} with m equal to n) prefers longest match. A quantified atom with other non-greedy quantifiers (including {m,n}? with m equal to n) prefers shortest match. A branch has the same preference as the first quantified atom in it which has a preference. An RE consisting of two or more branches connected by the | operator prefers longest match. Subject to the constraints imposed by the rules for matching the whole RE, subexpressions also match the longest or shortest possible substrings, based on their preferences, with subexpressions starting earlier in the RE taking priority over ones starting later. Note that outer subexpressions thus take priority over their component subexpressions. Note that the quantifiers {1,1} and {1,1}? can be used to force longest and shortest preference, respectively, on a subexpression or a whole RE. Match lengths are measured in characters, not collating elements. An empty string is considered longer than no match at all. For example, bb* matches the three middle characters of `abbbc', (week|wee)(night|knights) matches all ten characters of ` weeknights', when (.*).* is matched against abc the parenthesized subexpression matches all three characters, and when (a*)* is matched against bc both the whole RE and the parenthesized subexpression match an empty string. If case-independent matching is specified, the effect is much as if all case distinctions had vanished from the alphabet. When an alphabetic that exists in multiple cases appears as an ordinary character outside a bracket expression, it is effectively transformed into a bracket expression containing both cases, so that x becomes `[xX]'. When it appears inside a bracket expression, all case counterparts of it are added to the bracket expression, so that [x] becomes [xX] and [^x] becomes `[^xX]'.
5 3 3/25/2006 10:11:08 PM
If newline-sensitive matching is specified, . and bracket expressions using ^ will never match the newline character (so that matches will never cross newlines unless the RE explicitly arranges it) and ^ and $ will match the empty string after and before a newline respectively, in addition to matching at beginning and end of string respectively. ARE /A and /Z continue to match beginning or end of string only. If partial newline-sensitive matching is specified, this affects . and bracket expressions as with newline-sensitive matching, but not ^ and `$'. If inverse partial newline-sensitive matching is specified, this affects ^ and $ as with newline-sensitive matching, but not . and bracket expressions. This isn't very useful but is provided for symmetry. ◆LIMITS AND COMPATIBILITY(限制和兼容性) No particular limit is imposed on the length of REs. Programs intended to be highly portable should not employ REs longer than 256 bytes, as a POSIX-compliant implementation can refuse to accept such REs. The only feature of AREs that is actually incompatible with POSIX EREs is that / does not lose its special significance inside bracket expressions. All other ARE features use syntax which is illegal or has undefined or unspecified effects in POSIX EREs; the *** syntax of directors likewise is outside the POSIX syntax for both BREs and EREs. Many of the ARE extensions are borrowed from Perl, but some have been changed to clean them up, and a few Perl extensions are not present. Incompatibilities of note include `/b', `/B', the lack of special treatment for a trailing newline, the addition of complemented bracket expressions to the things affected by newline-sensitive matching, the restrictions on parentheses and back references in lookahead constraints, and the longest/shortest-match (rather than first-match) matching semantics. The matching rules for REs containing both normal and non-greedy quantifiers have changed since early beta-test versions of this package. (The new rules are much simpler and cleaner, but don't work as hard at guessing the user's real intentions.) Henry Spencer's original 1986 regexp package, still in widespread use (e.g., in pre-8.1 releases of Tcl), implemented an early version of today's EREs. There are four incompatibilities between regexp's near-EREs (`RREs' for short) and AREs. In roughly increasing order of significance: In AREs, / followed by an alphanumeric character is either an escape or an error, while in RREs, it was just another way of writing the alphanumeric. This should not be a problem because there was no reason to write such a sequence in RREs.
5 4 3/25/2006 10:11:08 PM
{ followed by a digit in an ARE is the beginning of a bound, while in RREs, { was always an ordinary character. Such sequences should be rare, and will often result in an error because following characters will not look like a valid bound. In AREs, / remains a special character within `[]', so a literal / within [] must be written `//'. // also gives a literal / within [] in RREs, but only truly paranoid programmers routinely doubled the backslash. AREs report the longest/shortest match for the RE, rather than the first found in a specified search order. This may affect some RREs which were written in the expectation that the first match would be reported. (The careful crafting of RREs to optimize the search order for fast matching is obsolete (AREs examine all possible matches in parallel, and their performance is largely insensitive to their complexity) but cases where the search order was exploited to deliberately find a match which was not the longest/shortest will need rewriting.) ◆BASIC REGULAR EXPRESSIONS(基本正則表達式) BREs differ from EREs in several respects. `|', `+', and ? are ordinary characters and there is no equivalent for their functionality. The delimiters for bounds are /{ and `/}', with { and } by themselves ordinary characters. The parentheses for nested subexpressions are /( and `/)', with ( and ) by themselves ordinary characters. ^ is an ordinary character except at the beginning of the RE or the beginning of a parenthesized subexpression, $ is an ordinary character except at the end of the RE or the end of a parenthesized subexpression, and * is an ordinary character if it appears at the beginning of the RE or the beginning of a parenthesized subexpression (after a possible leading `^'). Finally, single-digit back references are available, and /< and /> are synonyms for [[:<:]] and [[:>:]] respectively; no other escapes are available.
5 5 3/25/2006 10:11:08 PM
■regsub命令
語法:regsub ?switchs? exp string subSpec varname regsub的第一個參數是一個整個表達式,第二個參數是一個輸入字符串,這一點和regexp命令完全一樣,也是當匹配時返回1,否則返回0。不過regsub用第三個參數的值來替換字符串string中和正規表達式匹配的部分,第四個參數被認爲是一個變量,替換後的字符串存入這個變量中。例如:
% regsub there "They live there lives " their x 1 % puts $x They live their lives
這裏there被用their替換了。
regsub命令也有幾個開關(switchs):
-nocase 意義同regexp命令中。
-all 沒有這個開關時,regsub只替換第一個匹配,有了這個開關,regsub將把所有匹配的地方全部替換。
-- 意義同regexp命令中。
5 6 3/25/2006 10:11:08 PM
■string命令
string命令的語法:string option arg ?arg...?
string命令具有強大的操作字符串的功能,其中的option選項多達20個。下面介紹其中常用的部分。
■1、string compare ?-nocase? ?-length int? string1 string2
把字符串string1和string2進行比較,返回值爲-1、0或1,分別對應string1小於、等於或大於string2。如果有 -length 參數,那麼只比較前 int 個字符,如果 int爲負數,那麼這個參數被忽略。 如果有 -nocase參數,那麼比較時不區分大小寫。
■2、string equal ?-nocase? ?-length int? string1 string2
把字符串string1和string2進行比較,如果兩者相同,返回值爲1,否則返回0。其他參數與8.5.1同。
■3、string first string1 string2 ?startindex?
在string2 中從頭查找與string1匹配的字符序列,如果找到,那麼就返回匹配的第一個字母所在的位置(0-based)。如果沒有找到,那麼返回-1。如果給出了startindex變量,那麼將從startindex處開始查找。例如:
% string first ab defabc 3 % string first ab defabc 4 -1
■4、string index string charIndex
返回string 中第charIndex個字符(0-based)。charIndex可以是下面的值:
整數n: 字符串中第n個字符(0-based)
end : 最後一個字符
end-整數n:倒數第n個字符。string index "abcd" end-1 返回字符'c'
如果charIndex小於0,或者大於字符串string的長度,那麼返回空。
5 7 3/25/2006 10:11:08 PM
例如:
% string index abcdef 2 c % string index abcdef end-2 d
■5、string last string1 string2 ?startindex?
參照3.唯一的區別是從後往前查找
■6、string length string
返回字符串string的長度.
■7、string match ?-nocase? pattern string
如果pattern 匹配string,那麼返回1,否則返回0.如果有-nocase參數,那麼就不區分大小寫.
在pattern 中可以使用通配符:
* 匹配string中的任意長的任意字符串,包括空字符串.
? 匹配string中任意單個字符
[chars] 匹配字符集合chars中給出的任意字符,其中可以使用 A-Z這種形式
/x 匹配單個字符x,使用'/'是爲了讓x可以爲字符*,-,[,].
例子:
% string match * abcdef 1 % string match a* abcdef 1 string match a?cdef abcdef 1 % string match {a[b-f]cdef} abcdef //注意一定藥用'{',否則TCL解釋器會把b-f當作命令名 1 //從而導致錯誤 % string match {a[b-f]cdef} accdef 1
5 8 3/25/2006 10:11:08 PM
■8、string range string first last
返回字符串string中從第first個到第last個字符的子字符串(0-based)。如果first<0,那麼first被看作0,如果last大於或等於字符串的長度,那麼last被看作end,如果first比last大,那麼返回空。
■9、string repeat string count
返回值爲:重複了string字符串count次的字符串。例如:
% string repeat "abc" 2
abcabc
■10、string replace string first last ?newstring?
返回值爲:從字符串string 中刪除了第first到第last個字符(0-based)的字符串,如果給出了newstring變量,那麼就用newstring替換從第first到第last個字符。如果first<0,那麼first被看作0,如果last大於或等於字符串的長度,那麼last被看作end,如果first比last大或者大於字符串string的長度或者last小於0,那麼原封不動的返回string 。
■11、string tolower string ?first? ?last?
返回值爲:把字符串string轉換成小寫後的字符串,如果給出了first和last變量,就只轉換first和last之間的字符。
■12、string toupper string ?first? ?last?
同11。轉換成大寫。
■13、string trim string ?chars?
返回值爲:從string字符串的首尾刪除掉了字符集合chars中的字符後的字符串。如果沒有給出chars,那麼將刪除掉spaces、tabs、newlines、carriage returns這些字符。例如:
% string trim "abcde" {a d e} bc % string trim " def > " def
5 9 3/25/2006 10:11:08 PM
■14、string trimleft string ?chars?
同13。不過只刪除左邊的字符。
■15、string trimright string ?chars?
同13。不過只刪除右邊的字符。
6 0 3/25/2006 10:11:08 PM
文件訪問
■文件名
TCL提供了豐富的文件操作的命令。通過這些命令你可以對文件名進行操作(查找匹配某一模式的文件)、以順序或隨機方式讀寫文件、檢索系統保留的文件信息(如最後訪問時間)。
CL中文件名和我們熟悉的windows表示文件的方法有一些區別:在表示文件的目錄結構時它使用'/',而不是'/',這和TCL最初是在UNIX下實現有關。比如C盤tcl目錄下的文件sample.tcl在TCL中這樣表示:C:/tcl/sample.tcl。
6 1 3/25/2006 10:11:08 PM
■基本文件輸入輸出命令
這個名爲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 }
以上過程非常象UNIX的grep命令, 你可以用兩個參數調用它,一個是模式,另一個是文件名,tgrep將打印出文件中所有匹配該模式的行。
下面介紹上述過程中用到的幾個基本的文件輸入輸出命令。
open name ?access?
open命令 以access方式打開文件name。返回供其他命令(gets,close等)使用的文件標識。如果name的第一個字符是“|”,管道命令被觸發,而不是打開文件。
文件的打開方式和我們熟悉的C語言類似,有以下方式:
r 只讀方式打開。文件必須已經存在。這是默認方式。
r+ 讀寫方式打開,文件必須已經存在。
w 只寫方式打開文件,如果文件存在則清空文件內容,否則創建一新的空文件。
w+ 讀寫方式打開文件,如文件存在則清空文件內容,否則創建新的空文件。
a 只寫方式打開文件,文件必須存在,並把指針指向文件尾。
a+ 只讀方式打開文件,並把指針指向文件尾。如文件不存在,創建新的空文件。
open命令返回一個字符串用於表識打開的文件。當調用別的命令(如:gets,puts,close,〕對打開的文件進行操作時,就可以使用這個文件標識符。TCL有三個特定的文件標識: stdin,stdout和stderr ,分別對應標準輸入、標準輸出和錯誤通道,任何時候你都可以使用這三個文件標識。
6 2 3/25/2006 10:11:08 PM
gets fileId ?varName? 讀fileId標識的文件的下一行,忽略換行符。如果命令中有varName就把該行賦給它,並返回該行的字符數(文件尾返回-1),如果沒有varName參數,返回文件的下一行作爲命令結果(如果到了文件尾,就返回空字符串)。
和gets類似的命令是read,不過read不是以行爲單位的,它有兩種形式:
read ?-nonewline? fileId 讀並返回fileId標識的文件中所有剩下的字節。如果沒有nonewline開關,則在換行符處停止。
read fileId numBytes 在fileId標識的文件中讀並返回下一個numbytes字節。
puts ?-nonewline? ?fileId? string puts命令把string寫到fileId中,如果沒有nonewline開關的話,添加換行符。fileId默認是stdout。命令返回值爲一空字符串。
puts命令使用C的標準I/O庫的緩衝區方案,這就意味着使用puts產生的信息不會立即出現在目標文件中。如果你想使數據立即出現在文件中,那你就調用flush命令:
flush fileId 把緩衝區內容寫到fileId標識的文件中,命令返回值爲空字符串。
flush命令迫使緩衝區數據寫到文件中。flush直到數據被寫完才返回。當文件關閉時緩衝區數據會自動flush。
close ?fileId? 關閉標識爲fileId的文件,命令返回值爲一空字符串。
這裏特別說明的一點是,TCL中對串口、管道、socket等的操作和對文件的操作類似,以上對文件的操作命令同樣適用於它們。
6 3 3/25/2006 10:11:08 PM
■隨機文件訪問
默認文件輸入輸出方式是連續的:即每個gets或 read命令返回的是上次gets或 read訪問位置後面的字節,每個puts命令寫數據是接着上次puts寫的位置接着寫。TCL提供了seek,tell和eof等命令使用戶可以非連續訪問文件。
每個打開的打開文件都有訪問點,即下次讀寫開始的位置。文件打開時,訪問點總是被設置爲文件的開頭或結尾,這取決於打開文件時使用的訪問模式。每次讀寫後訪問位置按訪問的字節數後移相應的位數。
可以使用seek命令來改變文件的訪問點:
seek fileId offset ?origin? 把fileId標識的文件的訪問點設置爲相對於origin偏移量爲offset的位置。origin可以是start,current,end,默認是start。命令的返回值是一空字符串。
例如:seek fileId 2000 改變fieleId標識的文件訪問點,以便下次讀寫開始於文件的第2000個字節。
seek的第三個參數說明偏移量從哪開始計算。第三個參數必爲start,current或end中的一個。start是默認值:即偏移量是相對文件開始處計算。current是偏移量從當前訪問位置計算。end是偏移量從文件尾開始計算。
tell fileId 返回fileId標識的文件的當前訪問位置。
eof fileId 如果到達fileId標識的文件的末尾返回1,否則返回0。
6 4 3/25/2006 10:11:08 PM
■當前工作目錄
TCL提供兩個命令來管理當前工作目錄:pwd和Cd。
pwd和UNIX下的pwd命令完全一樣, 沒有參數,返回當前目錄的完整路徑。
cd 命令也和UNIX命令也一樣,使用一個參數,可以把工作目錄改變爲參數提供的目錄。如果cd 沒使用參數,UNIX下,會把工作目錄變爲啓動TCL腳本的用戶的工作目錄,WINDOWS下會把工作目錄變爲windows操作系統的安裝目錄所在的盤的根目錄(如:C:/)。值得注意的是,提供給cd的參數中路徑中的應該用'/'而不是'/'。如 cd C:/TCL/lib。這是UNIX的風格。
6 5 3/25/2006 10:11:08 PM
■文件操作和獲取文件信息
TCL提供了兩個命令進行文件名操作:glob和file,用來操作文件或獲取文件信息。
glob命令採用一種或多種模式作爲參數,並返回匹配這個(些)模式的所有文件的列表,其語法爲:
glob ?switches? pattern ?pattern ...?
其中switches可以取下面的值:
-nocomplain :允許返回一個空串,沒有-nocomplain時,如果結果是空的,就返回錯誤。
-- :表示switches結束,即後面以'-'開頭的參數將不作爲switches。
glob命令的模式採用string match命令(見8.5.7節)的匹配規則。例如:
%glob *.c *.h main.c hash.c hash.h
返回當前目錄中所有.c或.h的文件名。 glob 還允許模式中包含' 括在花括號中間以逗號分開的多種選擇',例如 :
%glob {{src,backup}/*.[ch]} src/main.c src/hash.c src/hash.h backup/hash.c
下面的命令和上面的命令等價:
glob {src/*.[ch]} {backup/*.[ch]}
注意:這些例子中模式周圍的花括號是必須的,可以防止命令置換。在調用glob命令對應的C過程前這些括號會被TCL解釋器去掉。
如果glob的模式以一斜線結束,那將只匹配目錄名。例如:
glob */
只返回當前目錄的所有子目錄。
如果glob返回的文件名列表爲空,通常會產生一個錯誤。但是glob的在樣式參數之前的第一個參數是"-nocomplain"的話,這時即使結果爲空,glob也不會產生錯誤。
對文件名操作的第二個命令是file。file是有許多選項的常用命令,可以用來進行文件操作也可以檢索文件信息。這節討論與名字相關的選項,下一節描述其他選項。使用file命令時,我們會發現其中有很明顯的UNIX痕跡。
6 6 3/25/2006 10:11:08 PM
file atime name 返回一個十進制的字符串,表示文件name最後被訪問的時間。時間是以秒爲單位從1970年1月1日12:00AM開始計算。如果文件name 不存在或查詢不到訪問時間就返回錯誤。例:
% file atime license.txt 975945600
file copy ?-force? ?--? source target
file copy ?-force? ?--? source ?source ...? targetDir
這個命令把source中指明的文件或目錄遞歸的拷貝到目的地址targetDir,只有當存在-force選項時,已經存在的文件纔會被覆蓋。試圖覆蓋一個非空的目錄或以一個文件覆蓋一個目錄或以一個目錄覆蓋一個文件都會導致錯誤。--的含義和前面所說的一樣。
file delete ?-force? ?--? pathname ?pathname ... ? 這個命令刪除pathname指定的文件或目錄,當指定了-force時,非空的目錄也會被刪除。即使沒有指定-force,只讀文件也會被刪除。刪除一個不存在的文件不會引發錯誤。
file dirname name 返回name中最後一個“/”前的所有字符;如果 name 不包含“/”,返回“.”;如果name 中最後一個“/”是第name的第一個字符,返回“/”。
file executable name 如果name對當前用戶是可以執行的,就返回1,否則返回0。
file exists name 如果name存在於當前用戶擁有搜索權限的目錄下返回1,否則返回0。
file extension name 返回name中最後的“.”以後(包括這個小數點)的所有字符。如果name中沒有“.”或最後斜線後沒有“.”返回空字符。
file isdirectory name 如果name是目錄返回1,否則返回0。
file isfile name 如果name是文件返回1,否則返回0。
file lstat name arrayName 除了利用lstat內核調用代理stat內核調用之外,和file stat命令一樣,這意味着如果name是一個符號連接,那麼這個命令返回的是這個符號連接的信息而不是這個符號連接指向的文件的信息。對於不支持符號連接的操作系統,這個命令和和file stat命令一樣。
file mkdir dir ?dir ...? 這個命令和UNIX的mkdir命令類似,創建dir中指明的目錄。如果dir已經存在,這個命令不作任何事情,也不返回錯誤。不過如果試圖用一個目錄覆蓋已經存在的一個文件會導致錯誤。這個命令順序處理各個參數,如果發生錯誤的話,馬上退出。
file mtime name 返回十進制的字符串,表示文件name最後被修改的時間。時間是以秒爲單位從1970年1月1日12:00AM開始計算。
file owned name 如果name被當前用戶擁有,返回1,否則返回0。
6 7 3/25/2006 10:11:08 PM
file readable name 如果當前用戶可對name進行讀操作,返回1,否則返回0。
file readlink name 返回name代表的符號連接所指向的文件。如果name 不是符號連接或者找不到符號連接,返回錯誤。在不支持符號連接的操作系統(如windows)中選項readlink沒有定義。
file rename ? -force? ?--? source target
file rename ?-force? ?--? source ?source ...? targetDir
這個命令同時具有重命名和移動文件(夾)的功能。把source指定的文件或目錄改名或移動到targetDir下。 只有當存在-force選項時,已經存在的文件纔會被覆蓋。 試圖覆蓋一個非空的目錄或以一個文件覆蓋一個目錄或以一個目錄覆蓋一個文件都會導致錯誤。
file rootname name 返回name中最後“.”以前(不包括這個小數點)的所有字符。如果name中沒有“.”返回Name。
file size name 返回十進制字符串,以字節表示name的大小。如果文件不存在或得不到name的大小,返回錯誤。
file stat name arrayName 調用stat內核來訪問name,並設置arrayName參數來保存stat的返回信息。 arrayName被當作一個數組,它將有以下元素:atime、ctime、dev、gid、ino、mode、mtime、nlink、size、type和uid。除了type以外,其他元素都是十進制的字符串,type元素和file type命令的返回值一樣。其它各個元素的含義如下:
atime 最後訪問時間.
ctime 狀態最後改變時間.
dev 包含文件的設備標識.
gid 文件組標識.
ino 設備中文件的序列號.
mode 文件的mode比特位.
mtime 最後修改時間.
nlink 到文件的連接的數目.
size 按字節表示的文件尺寸.
uid 文件所有者的標識.
6 8 3/25/2006 10:11:08 PM
這裏的atime、mtime、size元素與前面討論的file的選項有相同的值。要了解其他元素更多的信息,就查閱stat系統調用的文件;每個元都直接從相應stat返回的結構域中得到。 文件操作的stat選項提供了簡單的方法使一次能獲得一個文件的多條信息。這要比分多次調用file來獲得相同的信息量要顯著的快。
file tail name 返回name中最後一個斜線後的所有字符,如果沒有斜線返回name。
file type name 返回文件類型的字符串,返回值可能是下列中的一個: file、directory、characterspecial、blockSpecial、fifo、link或socket。
file writable name
如果當前用戶對name可進行寫操作,返回1,否則返回0。
6 9 3/25/2006 10:11:08 PM
錯誤和異常
■錯誤
錯誤和異常處理機制是創建大而健壯的應用程序的必備條件之一,很多計算機語言都提供了錯誤和異常處理機制,TCL也不例外。
錯誤(Errors)可以看作是異常(Exceptions)的特例。TCL中,異常是導致腳本被終止的事件,除了錯誤還包括break、continue 和return等命令。TCL允許程序俘獲異常,這樣僅有程序的一部分工作被撤銷。程序腳本俘獲異常事件以後,可以忽略它,或者從異常中恢復。如果腳本無法恢復此異常,可以把它重新發布出去。下面是與異常有關的TCL命令:
catch command ?varName? 這個命令把command作爲TCL腳本求值,返回一個整型值表明command結束的狀態。如果提供varName參數,TCL將生成變量varName,用於保存command產生的錯誤消息。
error message ?info? ?code? 這個命令產生一個錯誤,並把message作爲錯誤信息。如 峁﹊nfo參數,則被用於初始化全局變量errorInfo。如果提供code參數,將被存儲到全局變量errorCode中。
return -code code ?-errorinfo info? ?-errorcode errorCode? ?string? 這個命令使特定過程返回一個異常。code指明異常的類型,必須是ok,error,return,break,continue或者是一個整數。-errorinfo選項用於指定全局變量errorInfo 的初始值,-errorcode用於指定全局變量errorCode的初始值。 string給出return的返回值或者是相關的錯誤信息,其默認值爲空。
當發生一個TCL錯誤時,當前命令被終止。如果這個命令是一大段腳本的一部分,那麼整個腳本被終止。如果一個TCL過程在運行中發生錯誤,那麼過程被終止,同時調用它的過程,以至整個調用棧上的活動過程都被終止,並返回一個錯誤標識和一段錯誤描述信息。
舉個例子,考慮下面腳本,它希望計算出列表元素的總和:
set list {44 16 123 98 57} set sum 0 foreach el $list { set sum [expr $sum+$element] } => can't read "element": no such variable
這個腳本是錯誤的,因爲沒有element這個變量。TCL分析expr命令時,會試圖用element變量的值進行替換,但是找不到名字爲element的變量,所以會報告一個錯誤。由於foreach命令利用TCL解釋器解釋循環體,所以錯誤標識被返回給foreach。foreach收到這個錯誤,會終止循環的執行,然後把同樣的錯誤標識作爲它自己的返回值返回給調用者。按這樣的順序,
7 0 3/25/2006 10:11:08 PM
將致使整個腳本終止。錯誤信息can't read "element": no such variable會被一路返回,並且很可能被顯示給用戶。
很多情況下,錯誤信息提供了足夠的信息爲你指出哪裏以及爲什麼發生了錯誤。然而,如果錯誤發生在一組深層嵌套的過程調用中,僅僅給出錯誤信息還不能爲指出哪裏發生了錯誤提供足夠信息。爲了幫助我們指出錯誤的位置,當TCL撤銷程序中運行的命令時,創建了一個跟蹤棧,並且把這個跟蹤棧存儲到全局變量errorInfo中。跟蹤棧中描述了每一層嵌套調用。例如發生上面的那個錯誤後,errorInfo有如下的值:
can't read "element": no such variable while executing "expr $sum+$element" ("foreach" body line 2) invoked from within "foreach el $list { set sum [expr $sum+$element] }"
在全局變量errorCode中,TCL還提供了一點額外的信息。errorCode變量是包含了一個或若干元素的列表。第一個元素標示了錯誤類別,其他元素提供更詳細的相關的信息。不過,errorCode變量是TCL中相對較新的變量,只有一部分處理文件訪問和子過程的命令會設置這個變量。如果一個命令產生的錯誤沒有設置errorCode變量,TCL會填一個NONE值。
當用戶希望得到某一個錯誤的詳細的信息,除了 命令返回值中的錯誤信息外,可以查看全局變量errorInfo和errorCode的值。
7 1 3/25/2006 10:11:08 PM
■從TCL腳本中產生錯誤
大多數TCL錯誤是由實現TCL解釋器的C代碼和內建命令的C代碼產生的。然而,通過執行TCL命令error產生錯誤也是可以的,見下面的例子:
if {($x<0)||($x>100)} { error "x is out of range ($x)" }
error命令產生了一個錯誤,並把它的參數作爲錯誤消息。
作爲一種編程的風格,你應該只在迫不得已終止程序時下才使用error命令。如果你認爲錯誤很容易被恢復而不必終止整個腳本,那麼使用通常的return機制聲明成功或失敗會更好(例如,命令成功返回某個值,失敗返回另一個值,或者設置變量來表明成功或失敗)。儘管從錯誤中恢復是可能的,但恢復機制比通常的return返回值機制要複雜。因此,最好是在你不想恢復的情況下才使用error命令。
7 2 3/25/2006 10:11:08 PM
■使用catch捕獲錯誤
錯誤通常導致所有活動的TCL命令被終止,但是有些情況下,在錯誤發生後繼續執行腳本是有用的。例如,你用unset取消變量x的定義,但執行unset時,x可能不存在。如果你用unset取消不存在的變量,會產生一個錯誤:
% unset x can't unset "x": no such variable
此時,你可以用catch命令忽略這個錯誤:
% catch {unset x} 1
catch的參數是TCL腳本。如果腳本正常完成,catch返回0。如果腳本中發生錯誤,catch會俘獲錯誤(這樣保證catch本身不被終止掉)然後返回1表示發生了錯誤。上面的例子忽略unset的任何錯誤,這樣如果x存在則被取消,即使x以前不存在也對腳本沒有任何影響。
catch命令可以有第二個參數。如果提供這個參數,它應該是一個變量名,catch把腳本的返回值或者是出錯信息存入這個變量。
%catch {unset x} msg 1 %set msg can't unset "x": no such variable
在這種情況下,unset命令產生錯誤,所以msg被設置成包含了出錯信息。如果變量x存在,那麼unset會成功返回,這樣catch的返回值爲0,msg存放unset命令的返回值,這裏是個空串。如果在命令正常返回時,你想訪問腳本的返回值,這種形式很有用;如果你想在出錯時利用錯誤信息做些什麼,如產生log文件,這種形式也很有用。
7 3 3/25/2006 10:11:08 PM
■其他異常
錯誤不是導致運行中程序被終止的唯一形式。錯誤僅是被稱爲異常的一組事件的一個特例。除了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 "all done"} string 2 %set string all done
下表是對命令: catch command ?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來正確處理異常:
proc do {varName first last body} { global errorInfo errorCode upvar $varName v for {set v $first} {$v <= $last} {incr v} { switch [catch {uplevel $body} string] { 1 {return -code error -errorInfo $errorInfo /
7 4 3/25/2006 10:11:08 PM
-errorcode $errorCode $string} 2 {return -code return $string} 3 return } } }
這個新的實現在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值。
7 5 3/25/2006 10:11:08 PM
深入TCL
■查詢數組中的元素
利用array命令可以查詢一個數組變量中已經定義了的元素的信息。array命令的形式如下:
array option arrayName ?arg arg ...?
由於option的不同,array命令有多種形式。
如果我們打算開始對一個數組的元素進行查詢,我們可以先啓動一個搜索(search),這可以由下面的命令做到:
array startserach arrayName 這個命令初始化一個對name數組的所有元素的搜索(search),返回一個搜索標識(search identifier),這個搜索標識將被用於命令array nextelement、array anymore和array donesearch。
array nextelement arrayName searchId 這個命令返回arrayName的下一個元素,如果arrayName的所有元素在這一次搜索中都已經返回,那麼返回一個空字符串。 搜索標識searchId必須是array startserach的返回值。注意:如果對arrayName的元素進行了添加或刪除,那麼所有的搜索都會自動結束,就象調用了命令array donesearch一樣,這樣會導致array nextelement操作失敗。
array anymore arrayName searchId 如果在一個搜索中還有元素就返回1,否則返回0。 searchId同上。這個命令對具有名字爲空的元素的數組尤其有用,因爲這時從array nextelement中不能確定一個搜索是否完成。
array donesearch arrayName searchId 這個命令中止一個搜索,並銷燬和這個搜索有關的所有狀態。searchId同上。命令返回值爲一個空字符串。當一個搜索完成時一定要注意調用這個命令。
array命令的其他option如下:
array exists arrayName 如果存在一個名爲arrayName的數組,返回1,否則返回0。
array get arrayName ?pattern? 這個命令的返回值是一個元素個數爲偶數的的list。我們可以從前到後把相鄰的兩個元素分成一個個數據對,那麼,每個數據對的第一個元素是arrayName中元素的名字,數據對的第二個元素是該數據元素的值。數據對的順序沒有規律。如果沒有pattern參數,那麼數組的所有元素都包含在結果中,如果有pattern參數,那麼只有名字和pattern匹配(用string match的匹配規則)的元素包含在結果中。如果arrayName不是一個數組變量的名字或者數組中沒有元素,那麼返回一個空list。例:
7 6 3/25/2006 10:11:08 PM
% set b(first) 1 1 % set b(second) 2 2 % array get b second 2 first 1
array set arrayName list 設置數組arrayName的元素的值。 list 的 形式和array get的返回值的list形式一樣。如果arrayName不存在,那麼生成arrayName。例:
% array set a {first 1 second 2} % puts $a(first) 1 % array get a second 2 first 1
array names arrayName ?pattern? 這個命令返回數組arrayName中和模式pattern匹配的元素的名字組成的一個list。如果沒有pattern參數,那麼返回所有元素。如果數組中沒有匹配的元素或者arrayName不是一個數組的名字,返回一個空字符串。
array size arrayName 返回代表數組元素個數的一個十進制的字符串,如果 arrayName 不是一個數組的名字,那麼返回0。
下面這個例子通過使用array names 和foreach命令,枚舉了數組所有的元素:
foreach i [array names a] { puts "a($i)=$a($i)" }
當然,我們也可以利用startsearch、anymore、nextelement、和donesearch選項來遍歷一個數組.這種方法比上面所給出的foreach方法的效率更高,不過要麻煩得多,因此不常用。
7 7 3/25/2006 10:11:08 PM
■info命令
info命令提供了查看TCL解釋器信息的手段,它有超過一打的選項,詳細說明請參考下面幾節。
■變量信息
info命令的幾個選項提供了查看變量信息的手段。
info exists varName 如果名爲varName的變量在當前上下文(作爲全局或局部變量)存在,返回1,否則返回0。
info globals ?pattern? 如果沒有pattern參數,那麼返回包含所有全局變量名字的一個list。如果有pattern參數,就只返回那些和pattern匹配的全局變量(匹配的方式和string match相同)。
info locals ?pattern? 如果沒有pattern參數,那麼返回包含所有局部變量(包括當前過程的參數)名字的一個list,global和upvar命令定義的變量將不返回。如果有pattern參數,就只返回那些和pattern匹配的局部變量(匹配的方式和string match相同)。
info vars ?pattern? 如果沒有pattern參數,那麼返回包括局部變量和可見的全局變量的名字的一個list。如果有pattern參數,就只返回和模式pattern匹配的局部變量和可見全局變量。模式中可以用namespace來限定範圍,如:foo::option*,就只返回namespace中和option*匹配的局部和全局變量。 (注:tcl80以後引入了namespace概念,不過我們一般編寫較小的TCL程序,可以對namespace不予理睬,用興趣的話可以查找相關資料。)
下面針對上述命令舉例,假設存在全局變量global1和global2,並且有下列的過程存在:
proc test {arg1 arg2} { global global1 set local1 1 set local2 2 ... }
然後在過程中執行下列命令:
% info vars global1 arg1 arg2 local2 local1 //global2不可見 % info globals global2 global1 % info locals arg1 arg2 local2 local1
7 8 3/25/2006 10:11:08 PM
% info vars *al* global1 local2 local1
■過程信息
info 命令的另外的一些選項可以查看過程信息。
info procs ?pattern? 如果沒有pattern參數,命令返回當前namespace中定義的所有過程的名字。如果有pattern參數,就只返回那些和pattern匹配的過程的名字(匹配的方式和string match相同)。
info body procname 返回過程procname的過程體。 procname必須是一個TCL過程。
info args procname 返回包含過程procname的所有參數的名字的一個list。 procname必須是一個TCL過程。
info default procname arg varname procname必須是一個TCL過程, arg必須是這個過程的一個變量。如果arg沒有缺省值,命令返回0;否則返回1,並且把arg的缺省值賦給變量varname。
info level ?number? 如果沒有number參數,這個命令返回當前過程在調用棧的位置。如果有number參數,那麼返回的是包含 在調用棧的位置爲number的過程的過程名及其參數的一個list。
下面針對上述命令舉例:
proc maybeprint {a b {c 24}} { if {$a<$b} { puts stdout "c is $c" } } % info body maybeprint if {$a<$b} { puts stdout "c is $c" } % info args maybeprint a b c % info default maybeprint a x 0 % info default maybeprint a c 1 %set x 24
7 9 3/25/2006 10:11:08 PM
下面的過程打印出了當前的調用棧,並顯示了每一個活動過程名字和參數:
proc printStack{}{ set level [info level] for {set i 1} {$i<$level} {incr i} { puts "Level $i:[info level $i]" } }
■命令信息
info 命令的另外選項可以查看命令信息。
info commands ?pattern? 如果沒有參數pattern,這個命令返回包含當前namspace中所有固有、擴展命令以及以proc命令定義的過程在內的所有命令的名字的一個list。pattern參數的含義和info procs一樣。
info cmdcount 返回了一個十進制字符串,表明多少個命令曾在解釋器中執行過。
info complete command 如果命令是command完整的,那麼返回1,否則返回0。這裏判斷命令是否完整僅判斷引號,括號和花括號是否配套。
info script 如果當前有腳本文件正在Tcl解釋器中執行,則返回最內層處於激活狀態的腳本文件名;否則將返回一個空的字符串。
■TCL的版本和庫
info tclversion 返回爲Tcl解釋器返回的版本號,形式爲major.minor,例如8.3。
info library 返回Tcl庫目錄的完全路徑。這個目錄用於保存Tcl所使用的標準腳本,TCL在初始化時會執行這個目錄下的腳本。
■命令的執行時間
TCL提供time命令來衡量TCL腳本的性能:
time script ?count? 這個命令重複執行script腳本count次。 再把花費的總時間的用count除,返回一次的平均執行時間,單位爲微秒。如果沒有count參數,就取執行一次的時間。
8 0 3/25/2006 10:11:08 PM
■跟蹤變量
TCL提供了trace命令來跟蹤一個或多個變量。如果已經建立對一個變量的跟蹤,則不論什麼時候對該變量進行了讀、寫、或刪除操作,就會激活一個對應的Tcl命令,跟蹤可以有很多的用途:
1.監視變量的用法(例如打印每一個讀或寫的操作)。
2.把變量的變化傳遞給系統的其他部分(例如一個TK程序中,在一個小圖標上始終顯示某個變量的當前值)。
3.限制對變量的某些操作(例如對任何試圖用非十進制數的參數來改變變量的值的行爲產生一個錯誤。)或重載某些操作(例如每次刪除某個變量時,又重新創建它)。
trace命令的語法爲:
trace option ?arg arg ...?
其中option有以下幾種形式:
trace variable name ops command 這個命令設置對變量name的一個跟蹤:每次當對變量name作ops操作時,就會執行command命令。name可以是一個簡單變量,也可以是一個數組的元素或者整個數組。
ops可以是以下幾種操作的一個或幾個的組合:
r 當變量被讀時激活command命令。
w 當變量被寫時激活command命令。
u 當變量被刪除時激活command命令。通過用unset命令可以顯式的刪除一個變量,一個過程調用結束則會隱式的刪除所有局部變量。當刪除解釋器時也會刪除變量,不過這時跟蹤已經不起作用了。
當對一個變量的跟蹤被觸發時,TCL解釋器會自動把三個參數添加到命令command的參數列表中。這樣command實際上變成了
command name1 name2 op
其中op指明對變量作的什麼操作。name1和name2用於指明被操作的變量: 如果變量是一個標量,那麼name1給出了變量的名字,而name2是一個空字符串;如果變量是一個數組的一個元素,那麼name1給出數組的名字,而name2給出元素的名字;如果變量是整個數組,那麼name1給出數組的名字而name2是一個空字符串。爲了讓你很好的理解上面的敘述,下面舉一個例子:
8 1 3/25/2006 10:11:08 PM
trace variable color w pvar trace variable a(length) w pvar proc pvar {name element op} { if {$element !=""} { set name ${name}($element) } upvar $name x puts "Variable $name set to $x" }
上面的例子中,對標量變量color和數組元素a(length)的寫操作都會激活跟蹤操作pvar。我們看到過程pvar有三個參數,這三個參數TCL解釋器會在跟蹤操作被觸發時自動傳遞給pvar。比如如果我們對color的值作了改變,那麼激活的就是pvar color "" w。我們敲入:
% set color green Variable color set to green green
command將在和觸發跟蹤操作的代碼同樣的上下文中執行:如果對被跟蹤變量的訪問是在一個過程中,那麼command就可以訪問這個過程的局部變量。比如:
proc Hello { } { set a 2 trace variable b w { puts $a ;list } set b 3 } % Hello 2 3
對於被跟蹤變量的讀寫操作,command是在變量被讀之後,而返回變量的值之前被執行的。因此,我們可以在command對變量的值進行改變,把新值作爲讀寫的返回值。而且因爲在執行command時,跟蹤機制會臨時失效,所以在command中對變量進行讀寫不會導致command被遞歸激活。例如:
% trace variable b r tmp % proc tmp {var1 var2 var3 } { upvar $var1 t incr t 1 } % set b 2 2 % puts $b 3
8 2 3/25/2006 10:11:08 PM
% puts $b 4
如果對讀寫操作的跟蹤失敗,即command失敗,那麼被跟蹤的讀寫操作也會失敗,並且返回和command同樣的失敗信息。利用這個機制可以實現只讀變量。下面這個例子實現了一個值只能爲正整數的變量:
trace variable size w forceInt proc forceInt {name element op} { upvar $name x if ![regexp {^[0-9]*$} $x] { error "value must b a postive integer" } }
如果一個變量有多個跟蹤信息,那麼各個跟蹤被觸發的先後原則是:最近添加的跟蹤最先被觸發,如果有一個跟蹤發生錯誤,後面的跟蹤就不會被觸發。
trace vdelete name ops command 刪除對變量name的ops操作的跟蹤。返回值爲一個空字符串。
trace vinfo name 這個命令返回對變量的跟蹤信息。返回值是一個list,list的每個元素是一個子串,每個子串包括兩個元素:跟蹤的操作和與操作關聯的命令。如果變量name不存在或沒有跟蹤信息,返回一個空字符串。
■命令的重命名和刪除
rename 命令可以用來重命名或刪除一個命令。
rename oldName newName 把命令oldName改名爲newName,如果newName爲空,那麼就從解釋器中刪除命令oldName。
下面的腳本刪除了文件I/O命令:
foreach cmd {open close read gets puts} { rename $cmd {} }
任何一個Tcl命令都可以被重命名或者刪除,包括內建命令以及應用中定義的過程和命令。重命名一個內建命令可能會很有用,例如,exit命令在Tcl中被定義爲立即退出過程。如果某個應用希望在退出前獲得清除它內部狀態的機會,那麼可以這樣作:
rename exit exit.old proc exit status { application-specific cleanup
8 3 3/25/2006 10:11:08 PM
... exit.old $status }
在這個例子中,exit命令被重命名爲exit.old,並且定義了新的exit命令,這個新命令作了應用必需的清除工作而後調用了改了名字的exit命令來結束進程。這樣在已存在的描述程序中調用exit時就會有機會做清理應用狀態的工作。
■unknown命令
unknown命令的語法爲:
unknown cmdName ?arg arg ...? 當一個腳本試圖執行一個不存在的命令時,TCL解釋器會激活unknown命令,並把那個不存在的命令的名字和參數傳遞給unknown命令。unknown命令不是TCL的核心的一部分,它是由TCL腳本實現的,可以在TCL安裝目錄的lib子目錄下的init.tcl文件中找到其定義。
unknown命令具有以下功能:
1。如果命令是一個在TCL的某個庫文件(這裏的庫文件指的是TCL目錄的lib子目錄下的TCL腳本文件)中定義的過程,則加載該庫並重新執行命令,這叫做“auto-loading”(自動加載),關於它將在下一節描述。
2。如果存在一個程序的名字與未知命令一致,則調用exec命令來調用該程序,這項特性叫做“auto-exec”(自動執行)。例如你輸入“dir”作爲一個命令,unknown會執行“exec dir”來列出當前目錄的內容,如果這裏的命令沒有特別指明需要輸入輸出重定向,則自動執行功能會使用當前Tcl應用所擁有的標準輸入輸出流,以及標準錯誤流,這不同於直接調用exec命令,但是提供了在Tcl應用中直接執行其他應用程序的方法。
3。如果命令是一組特殊字符,將會產生一個新的調用,這個調用的內容是歷史上已經執行過的命令。例如,如果命令時“!!”則上一條剛執行過的命令會再執行一遍。下一章將詳細講述該功能。
4。若命令是已知命令的唯一縮寫,則調用對應的全名稱的正確命令。在TCL中允許你使用命令名的縮寫,只要縮寫唯一即可。
如果你不喜歡unknown的缺省的行爲,你也可以自己寫一個新版本的unknown或者對庫中已有unknown的命令進行擴展以增加某項功能。如果你不想對未知命令做任何處理,也可以刪除unknown,這樣當調用到未知命令的時候就會產生錯誤。
■自動加載
在unknown過程中一項非常有用的功能就是自動加載,自動加載功能允許你編寫一組Tcl過程放到一個腳本文件中,然後把該文件放到庫目錄之下,當程序調用這些過程的時候,第一次調用時由於命令還不存在就會進入unknown命令,而unknown則會找到在哪個庫文件中包含了
8 4 3/25/2006 10:11:08 PM
這個過程的定義,接着會加載它,再去重新執行命令,而到下次使用剛纔調用過的命令的時候,由於它已經存在了,從而會正常的執行命令,自動加載機制也就不會被再次啓動。
自動加載提供了兩個好處,首先,你可以把有用的過程建立爲過程庫,而你無需精確知道過程的定義到底在哪個源文件中,自動加載機制會自動替你尋找,第二個好處在於自動加載是非常有效率的,如果沒有自動加載機制你將不得不在TCL應用的開頭使用source命令來加載所有可能用到的庫文件,而應用自動加載機制,應用啓動時無需加載任何庫文件,而且有些用不到的庫文件永遠都不會被加載,既縮短了啓動時間又節省了內存。
使用自動加載只需簡單的按下面三步來做:
第一,在一個目錄下創建一組腳本文件作爲庫,一般這些文件都以".tcl"結尾。每個文件可以包含任意數量的過程定義。建議儘量減少各腳本文件之間的關聯,讓相互關聯的過程位於同一個文件中。爲了能夠讓自動加載功能正確運行,proc命令定義一定要頂到最左邊,並且與函數名用空格分開,過程名保持與proc在同一行上。
第二步,爲自動加載建立索引。啓動Tcl應用比如tclsh,調用命令auto_mkindex dir pattern , 第一個參數是目錄名,第二個參數是一個模式。auto_mkindex在目錄dir中掃描文件名和模式pattern匹配的文件,並建立索引以指出哪些過程定義在哪些文件中,並把索引保存到目錄dir下一個叫tclindex的文件中。如果修改了文件或者增減過程,需要重新生成索引。
第三步是在應用中設置變量auto_path,把存放了希望使用到的庫所在的目錄賦給它。auto_path變量包含了一個目錄的列表,當自動加載被啓動的時候,會搜索auto_path中所指的目錄,檢查各目錄下的tclindex文件來確認過程被定義在哪個文件中。如果一個函數被定義在幾個庫中,則自動加載使用在auto_path中靠前的那個庫。
例如,若一個應用使用目錄/usr/local/tcl/lib/shapes下的庫,則在啓動描述中應增加:
set auto_path [linsert $auto_path 0 /usr/local/tcl/lib/shapes]
這將把/usr/local/tcl/lib/shapes作爲起始搜索庫的路徑,同時保持所有的Tcl/Tk庫不變,但是在/usr/local/tcl/lib/shapes中定義的過程具有更高的優先級,一旦一個含有索引的目錄加到了auto_path中,裏面所有的過程都可以通過自動加載使用了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章