TCL - info命令

Augusdi發表於2015-10-13


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中,裡面所有的過程都可以通過自動載入使用了。

相關文章