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
% 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
下面的過程打印出了當前的調用棧,並顯示了每一個活動過程名字和參數:
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參數,就取執行一次的時間。
■跟蹤變量
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是一個空字符串。爲了讓你很好的理解上面的敘述,下面舉一個例子:
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
% 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
...
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則會找到在哪個庫文件中包含了這個過程的定義,接着會加載它,再去重新執行命令,而到下次使用剛纔調用過的命令的時候,由於它已經存在了,從而會正常的執行命令,自動加載機制也就不會被再次啓動。
自動加載提供了兩個好處,首先,你可以把有用的過程建立爲過程庫,而你無需精確知道過程的定義到底在哪個源文件中,自動加載機制會自動替你尋找,第二個好處在於自動加載是非常有效率的,如果沒有自動加載機制你將不得不在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中,裏面所有的過程都可以通過自動加載使用了。