<tcl_tk> TCL介紹

2,995 views
Skip to first unread message

Murphy Chen

unread,
Aug 16, 2007, 10:46:59 AM8/16/07
to cs_...@googlegroups.com
作者: defar (天邊之柏) 看板: TclTk
標題: TCL介紹
時間: Wed Nov 15 08:36:53 2006

轉載  http://ljh.ee.nchu.edu.tw/~cch/program/Tcl/index.html 呆子的臥龍居


Tcl/Tk

Why Tcl?
Tcl 簡單的語法讓你能夠快速發展程式。Perl/Python/PHP/Ruby 都是十分優秀的語言,並且在某一方面都非常的出色,但是 Tcl 是一個最「均衡」的語言,在大部份的領域上都十分的出色,特別是他原來就被設計為十分容易嵌入和擴展的語言,讓 Tcl 變成十分有威力的語言。


一種程式語言,代表著一種解決問題的思維,雖然 Python 有著發展大型程式的潛力,但是作為一個處理日常事務的 script language,是不是有需要預留大型程式的空間嗎?這似乎是見仁見智的問題,但是對我而言,Python 已經賦予一個 script language 太多的期望。


Script language 通常用來管理自己的作業環境(當然,還有許許多多的用途,Web Applications, Desktop GUI Applications, Testing and Automation, Databases, Embedded Development 等都是 script language 可以發揮的地方),因此在選擇上,應該選擇簡易好寫但是威力又
不弱的 script language,簡單的語法是有其侷限性,但是如果過於困難的任務,為什麼又一定要使用 script language 來實作呢?我們應該了解,這個世界上沒有可以全部適用的語言,在一個領域上極為突出,代表著他通常在另外一個領域上的缺陷上一定極大,期待一個語言上的萬
靈丹是不可行的。


Tcl 語法十分的有彈性(沒有內建的關鍵字)但是卻又十分的有規則(規則就是那幾條),語言的彈性遠超過 Python,支援 object oriented code(有許多的擴充套件可以做到這點), event-oriented programming, functional style code, and imperative code 等多個 paradigm�A具有 functional language 的一些特性(一般的語言想要回過頭來支援這個特性十分困難),不會強迫使用者一定要使用某種思考模式來解決問題,同時讓你可以輕鬆的擴充各種功能,我認為在 script language 上是一個好選擇。


下表是 MD5 algorithm Performance 比較表:


程式語言
版本
時間 (ms)
實作方式

C
GCC 2.95
0.24
Portable compiled C

iForth
0.35
0.35
Native-code Forth [*]

BigForth
1.5
1.5
Native-code Forth

GForth
0.5.0
15
GCC-specific optimized C

pForth
v21
59
Threaded VM in standard C

Ficl
2.04
84
Threaded VM with C subr's

Lua
4.0
150
Lua bytecode VM in C

Tcl
8.4a2
180
Tcl bytecode VM in C

Tcl
8.4a2 48
Tcl bytecode VM in C (based on inlining)

Perl
5.005
230
Perl bytecode VM in C

Perl
5.600
31
Perl bytecode VM in C (based on inlining)
Python
2.0
460
Python bytecode VM in C

Python
2.0
270
Python bytecode VM in C (based on inlining)

Introduction to Tcl
Tcl 是 Tool Command Language 的縮寫,是廣為使用的 script language,由 Dr. John Ousterhout 所創造。

Tcl 的中心思想是一切都是字串(或者也可以修正為,看起來都是字串),整個程式是由「Command」所組合起來的,沒有一般程式語言的變數型態,不用自己管理記憶體,解譯器被設計成一個 C 的函式庫,提供基本的命令與控制結構,語法清晰容易維護,並且使用 Tcl 的任何程式皆
可以根據 tcl 的規格撰寫 C 程式與之鏈結增加新的命令,以提高關鍵程式的效率、或增加新的特色,同時也很容易的就可以內嵌到其它的程式作為 script language,讓使用者可以訂製一些個人的設定與一些自動化的工作。





Tcl 附了一個交談式解譯器 tclsh,可以用來線上學習 tcl 之用,也可以用來除錯。而他的另外一個模式就是 script 模式。Tcl script 模式執行時一定是從第一行開始,由上而下,由左而右,而不像 C  是從 main() 這個 funcion 開始執行起。

Tcl 最基本的語法形式如下:

命令 參數1 參數2 ... 參數n
總結 Tcl 的重點語法
一個 Tcl Script 可以視為是一個包含很多 Tcl 命令的字串。分號與換行字元代表著個別命令的分隔。
分析參數,使用置換規則來對參數進行置換。
如果一個參數的第一個字元是大括號,則一直到對應的結束大括號出現為止, 該字串整個視為一個參數。其內部的字串不再進行置換。如果一個參數的第一個字元是雙引號,則一直到對應的結束雙引號出現為止,該字串整個視為一個參數。如果參數或是雙引號中間的字是以中括號起始
,則至另一個對應的結束中括號出現為止,視為一個命令,並且進行命令置換。
在設定值給變數時並未使用 $ 符號,只有在使用變數時才使用 $ 符號。如果參數,或是雙引號中間的字是以變數符號($)為起始,則至空白字元,或是其他一般變數所不允許的字元為止,該字串視為變數名稱,並且進行變數置換。 若變數符號後面緊接著是大括號,則至另一個對應結�籅漱j括號出現為止,中間 的字串無論任何字元均視為變數名稱的一部份。
若反斜線出現在參數或雙引號字串中,依據反斜線置換的規則進行置換。例如:\t \n 等代表的意義。反斜線放在最後,則代表換行。
若註解符號(#)出現在第一個字(不含空白字元),該行被視為註解。
上面各種置換Tcl直譯器進行處理時僅會處理一次。處理之後,會被一概視為普通字元。
置換後的空白字元不再視為參數的分隔。
舉例來說,Tcl的雙引號中允許命令置換及反斜線置換,如下面的例子:

puts "2*3-4 = [expr 2*3-4]"
Tcl 遇到第一個雙引號後,就會尋找下一個雙引號並視其為該字串的結尾,因此,不能在雙引號之中再使用一次雙引號,這時候,就必須要用反斜線置換:

puts "Tcl say \"Hello World!\""
在Tcl 中,另外一個可以括住字串的便是大括號。

puts {Hello World!}
被大括號括住的字串,整個被視為一個參數,不會有任何的置換發生。

puts {Hello World!\}}
在上個例子中,請注意,反斜線仍然會被留下來。

大括號的另一個重要的特性,大括號中若有其他大括號,則必需是成對的,除非有使用反斜線來表示大括號。


Tcl 之所以被認為具有 functional language 的一些特性,在於你可以這樣做:


proc combine {f g h} { $f [$g $h]}
其中 f g 等參數「可以」是 Tcl 的 command,Tcl 對這個並不會有任何的限制。


註解
Tcl 採用大部分的 Shell Script 所使用的註解符號 -- "#"。如果每行的第一個字元若為"#" (空白字元不算),則在註解字元後面的任何字元都會被視為註解。

變數
Tcl 變數的可以隨時建立不用事先宣告,變數名稱的取法也沒有任何限制,所以可以使用任何字元來為變數命名,甚至變數名跟指令名稱相同也沒問題。使用時只有一點需要注意,就是 Tcl 是大小寫有別的語言。我們通常以 set 指令來設定變數值。Tcl 的 set 指令除了設定變數值的
用途外,也可以用來取出變數的內容。

unset 指令用來刪除一個或多個變數,來釋放記憶體空間。

我們可以使用 info exists 指令來檢查變數是否存在。

if {![info exists counter]} {   set counter 0}
Tcl 在執行時會建立一些全域變數,可以利用 info global 列出有哪些全域變數。

當要使用變數的時候,除了設定,一般而言都必須使用 $ 來存取,例如:

set year "2005"puts $year
如果要進行數值運算,可以使用 expr 這個指令,例如說,

set a -5set resut [expr abs($a)]
我們也可以取得一個介於 0 與 1 之間的亂數,例如下面的例子,


set r [expr rand()]
字串
字串是 Tcl 語言最基本的資料型態,常見的字串處理指令有:string、append、format、scan 以及 binary。

string 指令的第一個參數代表對字串的操作方式。字串的比較儘量使用 string compare 或 string equal,因為如果 Tcl 會轉換字串為數字來比較,所以會有可能的潛在性錯誤。

下面是 string 這個指令的常用命令摘要:

string length 取得字串的長度
string compare 比較字串
string first 到第二個字串裡面去找第一個字串 (從左邊找起)
string last 到第二個字串裡面去找第一個字串 (從右邊找起)
string index 取出字串的第幾個字元
string range 取出字串的子字串
string tolower 轉成小寫
string toupper 轉成大寫
string trim 刪除兩邊的空格
string match 以 glob 方式檢查字串是否符合某個 "樣版"
regexp 以 regular expression 的方式檢查字串是否符合某個 pattern,regsub 以 regular expression (正規表示式)的方式把字串當中符合某個 pattern 的子字串代換掉,下面是一些使用的範例:

尋找 badger 這個字串位於 string 中的 index,並且把值傳給 location 這個變數。

regexp -indices {(?i)\<badger\>} $string location
下面是將 foo 取代為 bar 的範列,最後將結果放在 string 中。

regsub -all {\<foo\>} $string bar string
請注意,在簡單的字串運算(取得字串的大小,比較字串,取出字串的子字串等比較單純的字串運算)上,儘量不要使用正規表示式,因為這樣不但無法帶來效能上的提昇,反而可能會有效能上的問題。


append 指令用來將新的項目附加到指定變數的內容後,如下面所示範的:


set name "Danilo"append name " Raynor"
format 與C語言中的 printf 十分相似,依據指定的格式將字串格式化。

format "The square root of 10 is %.3f" [expr exp(10)]=> The square root of 10 is 3.162
%s
字串
%d 十進制整?
%f 實數

%e mantissa-exponent 表示的實?

%c
字元


scan 與C語言中的scanf十分相似,依據指定的格式化條件剖析字串並將結果放入變數中。

binary 可以從某個 binary string 中插入與取得某個 field。

陣列
陣列在 Tcl 中具有很重要的角色,而 Tcl 只有一維陣列的觀念,但是可以透過模擬而有多維陣列的效果(這點跟 C 很像,沒有多維陣列的概列,只有多個一維陣列組合而成的陣列)。Tcl 陣列的特點是可以使用字串作為陣列的索引值,而不只是單純的使用數字作為索引。

Set name(first) "Mary"Set name(last) "Poppins"
Tcl 可以使用 array 這個命令來取得與設定陣列,下面是一些相關的參數:


array exists arrayName: eturns 1 if arrayName is an array variable. Returns 0 if arrayName is a scalar variable, proc, or does not exist.
array names arrayName ?pattern:eturns a list of the indices for the associative array arrayName. If pattern is supplied, only those indices that match pattern are returned. The match is done using the globbing technique from string match.
array size arrayName:Returns the number of elements in array arrayName.
array get arrayName:Returns a list in which each odd member of the list (1, 3, 5, etc) is an index into the associative array. The list element following a name is the value of that array member.
array set arrayName dataList:Converts a list into an associative array. DataList is a list in the format of that returned by array get. Each odd member of the list (1, 3, 5, etc) is an index into the associative array, and the list element following
that is the value of that array member.

因為使用 array get 或 foreach 時,Tcl 的作法是先產生一個暫時性的串列,這樣會浪費一些記憶體空間與執行速度。改用 array 的搜尋功能可以加快陣列元素巡訪(iterate)的速度。

串列的處理
Tcl還有一種資料結構,叫做 list(串列),觀念上很簡單,最基本的觀念就是一串以空白隔開的字串。最基本的Tcl 命令語法,就是串列,所以,對於對於串列的掌握度越高,則越能發揮出 Tcl 的威力。通常我們會搭配 foreach 的使用,將 list 中的元素一一取出做運算。


下面是一些串列處理的例子:


set x "a b c"puts "Item 2 of the list {$x} is: [lindex $x 2]\n"set y [split 7/4/1776 "/"]puts "We celebrate on the [lindex $y 1]'th day of the [lindex $y 0]'th month\n"set z [list puts "arg 2 is $y" ]puts "A command resembles: $z\n"set i 0;foreach j $
x { puts "$j is item number $i in list x" incr i;}
Tcl 提供了下列的內建命令來處理串列:

concat - Join lists together
join - Create a string by joining together list elements
lappend - Append list elements onto a variable
lindex - Retrieve an element from a list
linsert - Insert elements into a list
list - Create a list
llength - Count the number of elements in a list
lrange - Return one or more adjacent elements from a list
lreplace - Replace elements in a list with new elements
lsearch - See if a list contains a particular element
lset - Change an element in a list
lsort - Sort the elements of a list
split - Split a string into a proper Tcl list
下面是使用串列的例子:

if {$argc > 0} {    set total $argv[1]} else {  set total 1000}set number 0set fileList [glob -type f sample.*]set length [llength $fileList]for {set number 0} {$number < $length} {incr number 1} {   set handlefile [lindex $fileList $number]       set
extName [file extension $handlefile]   set count 0     for {set count 1} {$count < $total} {incr count 1} {    set filename [format "TestSample%04d.%s" $count $extName]   file copy $handlefile $filename }}
Call by Reference
Tcl 中,可以使用 upvar 這個 command 來達到 c++ 中參考的效果。請注意,陣列無法以值傳遞,一定要用 upvar 來傳遞。

舉個例子來說,想寫一個副程式 swap 用來交換兩個變數的內容,可以這麼寫:

proc swap {a b} {   upvar $a valuea $b valueb   set tmp $valuea set valuea $valueb  set valueb $tmp return ""}
控制結構
if/switch 及 for/foreach/while這些條件敘述及迴圈敘述雖然很像C/C++, Java語言中的條件敘述及迴圈敘述。但是,實際上這些不過是一般的Tcl命令而已,因為Tcl 的中心思想是一切都是字串(由命令和參數組成)。要注意的是, { 大括號不能獨立一行,否則會被當成是下一個命
令的參數。

條件敘述 - if
在使用這些控制結構的時候,語法與 C/C++, Java 這些語言很像,只要記住一點,用大括號括起來的整個都會被當成是參數,這樣就可以正確的使用這些命令。if 用來判斷描述式是否正確 (true or false)。

if {$user_length > 0 && $message_length > 0} { db_dml danny:statement2 { insert into message (name, message) values (:user, :message); }}
條件敘述 - switch
這個控制結構和 C 的 switch 一樣,但是不只可以用數字,也可以用字串當作選項。

set var seeswitch -glob -- $var { {see} - {saw} - {seen} { puts "All mean the same thing!"}}
迴圈敘述 - while
while用來測試某一個句子,如果條件滿足,就會一直執行下去。break 與 continue 都是如同 C/C++ 的語法一般,都是一樣的作用。

set b ""set i [expr [llength $a] -1]while {$i >= 0} {   lappend b [lindex $a $i]    incr i -1}
迴圈敘述 - for
這也是一般語言常用的控制結構。

set b ""for {set i [expr [llength $a] -1]} {$i >=0} {incr i -1} {   lappend b [lindex $a $i]}
迴圈敘述 - foreach
這在 C/C++/Java 中並沒有這個控制結構,這在 Bash/Tcsh 等 shell 中比較常看到。這是把串列中的值一個一個的拿出來的一個迴圈用法。

set b ""foreach i $a {  set b [linset $b 0 $i]}
after

有時候,我們會需要等待一段時間才執行某些動作,這時候我們可以用到 after 這個命令。在 socket/File IO的處理上,常常配合 vwait 來進行處理。


Eval

我們可以用 Eval 這個指令將參數串接成一個字串後,將字串視為一個 Tcl Script 丟給解譯器去執行。請注意,使用這個指令的時候最好
用大括號合成一個字串,而不是運用雙引號,因為使用雙引號會讓指令被置換兩次,這樣可以避免指令分析的問題。


foreach i $vars { unset $i}eval unset $vars
Source
我們可以 include 另外一個 script file,如同下列的例子:

source function.tcl
錯誤處理
error message error message info error message info code
傳回一個錯誤,引起解釋器停止運行。info用於初始化全局變量 errorInfo。code(所傳回的值)會被設為 errorCode。

catch script ?varName?
當Tcl指令發生錯誤時,例如使用open開啟檔案失敗, Tcl會中斷目前執行的Tcl Script。我們可以使用catch指令來補捉這些錯誤,catch指令會執行指定的指令,如果指令沒有發生錯誤,則catch指令會將指令的傳回值放入第二個參數,或是在指令發生錯誤時將錯誤訊息放入第二個參�C

catch {unset x} msg
在這個例子中,msg 就是有錯誤發生時的錯誤訊息。

return -code code ?-errorinfo info? ?-errorcode code? ?string?
回傳一個錯誤值。

catch {return "all done"} string
在這個例子中,string 的值就是 "all done"。


我們也可以使用 catch 來判斷是否執行成功:


if {[catch {[open com1: r+]}]} { puts "Open Serial Port fail." exit 1}
程序
Tcl 的程序跟一般程式語言的程序一樣,是程式模組化的工具,當你定義好一個 procedure 時,它就會變成一個 Tcl 指令。

proc procName argList body
proc指令第一個參數是 procedure 的名稱,第二個參數是 procedure 的參數列,第三個參數則是 procedure 的指令主體,會在 procedure 被呼叫時執行起來。

關於程序的傳回值,除非你用return指令明示程序的傳回值,否則 Tcl 會將程序最後一行指令的執行結果當做是程序的傳回值。

set PI [expr 2 * asin(1.0)]proc cir_area {rad} { global PI return [expr $PI * $rad]}
在程序的變數空間 (Scope) 是看不到全域變數的,如果要在程序中存取全域變數,則必須先使用global來宣告才能存取。

我們也可以指定程序參數的預設值 (default value)。

set PI [expr 2 * asin(1.0)]proc cir_area {{rad 1}} { global PI return [expr $PI * $rad]}
透過 args 這個關鍵字,我們也可以定義可接受不定參數的程序 (Tcl 會將接受到的參數變成一個list放入args變數中)。


proc example {first {second ""} args} { if {$second == ""} {    puts "There is only one argument and it is: $first" return 1 } else { if {$args == ""} {    puts "There are two arguments - $first and $second" return 2    } else { puts "There are many a
rguments - $first and $second and $args" return "many"  } }}
在 Tcl 裏,我們可以使用 rename 指令將一個指令重新命名。


NameSpace
Tcl 目前支援 namespace 的概念,一個 namespace 是 function 和變數的集合,和  C++ 類似,透過封裝可以保證不會被其它的 namespace 的變數和命令所影響,而且你可以隨時新增、改名與刪除裡面的成員。


NameSpace 的成員使用 double colons(::) 來加以區隔,因此 global scope 也可以識別為 "::"。如果沒有使用 double colons 開頭,那麼就是位於目前這個 NameSpace (可能是 global namespace 或者是其它的 NameSpace)的 function 和變數。

namespace eval 允許你建立一個新的namespace,如同下面的例子:

namespace eval Counter {    variable num 0 }
Tcl 的 namespace 隨時可以新增和減少裡面的成員,如下面的例子,

namespace eval Counter {    variable num 0//初始化  proc Bump {} {  variable num    return [incr num]   } }# 新增 functionnamespace eval Counter {  proc test {args} {  return $args    } } # ?除 test namespace eval Counter {     rename test "" }
我們也可以 import 進其它 NameSpace 的 function 和變數到 global scope,如同下列使用 XOTcl 的例子:

package require XOTclnamespace import ::xotcl::*
命令列的參數和環境變數
下面是範例:

puts "There are $argc arguments to this script"puts "The name of this script is $argv0"if {$argc > 0} {puts "The other arguments are: $argv" }# [array names env] 會傳回包含全部的環境變數的串列puts "You have these environment variables set:"foreach ind
ex [array names env] {  puts "$index: $env($index)"}
當然,你也可以這樣寫來印出環境變數:

parray env
Socket 和檔案 IO
PWD
我們可以使用 PWD 這個命令取得目前所在的目錄。

File
我們可以用 file 來取得檔案與目錄的資訊,支援下面的功能:

string manipulation appropriate to parsing file names

dirname - Returns directory portion of path
extension - Returns file name extension
rootname - Returns file name without extension
tail - Returns filename without directory
information about an entry in a directory:

atime - Returns time of last access
executable - Returns 1 if file is executable by user
exists - Returns 1 if file exists
isdirectory - Returns 1 if entry is a directory
isfile - Returns 1 if entry is a regular file
lstat - Returns array of file status information
mtime - Returns time of last data modification
owned - Returns 1 if file is owned by user
readable - Returns 1 if file is readable by user
readlink - Returns name of file pointed to by a symbolic link
size - Returns file size in bytes
stat - Returns array of file status information
type - Returns type of file
writable - Returns 1 if file is writeable by user
如果要刪除檔案,可以使用下列的方式:

file delete pathName
如果要複製檔案,下面是一個使用的範例:

set number 1000for {set number 0} {$number <= 1000} {incr number 1} {   set filename [format "TestFile%04d.jpg" $number]    file copy 01.jpg $filename}
如果要建立新的資料夾,下面是一個使用的範例:


set number 1000for {set number 0} {$number <= 1000} {incr number 1} {   set foldername [format "Test%04d" $number]  file mkdir $foldername}
Glob
glob 可以讓你指定 pattern 來得到目錄內的檔案列表,就像是 unix 下的 ls 命令和 dos 下的 dir 指令。

Exec
exec 讓你可以執行指定的一個程式。


exec rm main.o
Process ids
set f [open {| tbl | ditroff -ms} w]pid $f
檔案 IO
Tcl 使用 open 這個命令開檔,再來使用 gets/puts 或者是 read/write/seek 等命令讀寫檔案。Tcl 視一個打開的檔案為 Channel(與 C 的 File * Stream,Tcl 的 Channel是類似的觀念)。
檔案讀寫的範例:

## Count the number of lines in a text file#set infile [open "myfile.txt" r]set number 0## gets with two arguments returns the length of the line,# -1 if the end of the file is found#while { [gets $infile line] >= 0 } { incr number}close $infileputs "
Number of lines: $number"## Also report it in an external file#set outfile [open "report.out" w]puts $outfile "Number of lines: $number"close $outfile
下面則是一個 Socket 的範例(Time Server):


proc Server {channel clientaddr clientport} { puts "Connection from $clientaddr registered" puts $channel [clock format [clock seconds]] close $channel}socket -server Server 9900vwait forever
一個 Socket 的範例(Time Client):


set server localhostset sockChan [socket $server 9900]gets $sockChan lineclose $sockChanputs "The time on $server is $line"
Virtual File System
Tcl provide an open ended and extensible system that allows you to access any such resource using the same I/O commands.

the native file system
zip files
web sites
ftp sites
WebDAV sites
TAR files
Metakit databases
available Tcl commands
基本的觀念,就是來自於 Unix,把一切都當成檔案來操作,使用 mount 這個觀念來載入檔案系統進行操作。

舉一個簡單的例子,從網路上 download 一個檔案(這裡是下載 Digital Mars 的 C++ 編譯器),就好像在自己的 Local Driver 操作一樣,完全使用一樣的指令:

package require vfs::ftpvfs::ftp::Mount ftp.digitalmars.com DIGITALMARSfile copy DIGITALMARS/Digital_Mars_C++/Patch/dm845c.zip c:/dm845c.zip
多國語言
Tcl 已經支援多國語言(International Programming, I18N)和 Unicode。請注意,但是 Tcl script file 本身不支援 Unicode,預計在 8.5 會納入 Tcl script file 本身對各國編碼的支援。

Tcl 已經支援多國語言(International Programming),方法是使用 msgcat 這個 command,他的使用概念是直接把一個字串當做是 index ,再去跟據系統目前設定的語系,找出翻譯好的字串替代。

source 功能和 BASH 的 source 類似,也和 C 的 include 類似。我們就利用這個函數去載入翻譯檔。簡單說,就是自己判斷語系,然後自行載入翻譯檔就對了。

如果沒有該語系的翻譯檔,它就什麼都不 翻譯,用你程式所寫的字串即是了。

#!/usr/bin/wish #package require msgcat # 載入翻譯檔的目錄路徑::msgcat::mcload "./msgs/"set language [::msgcat::mclocale] puts "language is $language" if [file isfile "./msgs/${language}.msg" ] {  source "./msgs/${language}.msg" } set hello_en "hello"
set ok "" set hello_s "" # 翻譯字串 namespace eval ::msgcat_exp1 {  set hello_s [::msgcat::mc "hello"]  set ok [::msgcat::mc "ok"] }# 先印原始字串 puts "$hello_en" # 再印翻譯字串 puts "$hello_s" puts "$ok" exit
中文可以建立一個 msgs/zh_tw.msg 檔案:

namespace eval ::msgcat_exp1 {  ::msgcat::mcset zh_tw "hello" "哈囉"  ::msgcat::mcset zh_tw "ok" "確定" }
mcload 會自動去輸入的路徑找相關語系的翻譯檔。

Serial Port 的支援
處理的程序大致上是這樣:

Open the serial port using [open]

Configure useful comms stuff with [fconfigure].
Use [fileevent] to get readable/writable callbacks.
And use [close] when you are done.

在 Tcl/Tk 8.4 中,已經加入了 serial port 的支援(目前支援Unix和Windows二個平台),使用的方法是將 serial port當成是一個檔案,並且對這個檔案(serial port)進行讀寫。下面是一個使用的範例:

if [string equal -nocase $tcl_platform(platform) {windows}] {  set comport com1: } else {  set comport /dev/ttyS0 } if { [catch {open $comport {r+}} fdin] } {  puts stderr "Error opening serial port $comport"  after 2000  exit }fconfigure $fdin -mode
38400,n,8,1 -blocking 0 -translation binary -buffering none
下面是另外一個範例,使用 fileevent 等待事件的發生,當 serial port 可以讀(readable)或者是可以寫的時候,我們的動作就會繼續下去。


############################################# AT Command/Modem Test Program############################################proc private_send_exp_reader {serial regexp} {     global TCL_RESULT     global waitSecs     flush $serial     after [expr $waitSecs
*1000]     if {[catch {set command_res [read $serial]}]} {        set TCL_RESULT 0       } else {           if {[regexp $regexp $command_res]} {           set TCL_RESULT 1                  } else {      set TCL_RESULT 0 }     }}proc send_exp_reader {s
erial outstr regexp seconds} {   global TCL_RESULT   puts -nonewline $serial $outstr   fileevent $serial readable [list private_send_exp_reader $serial $regexp]   after [expr {$seconds*1000}] [list set TCL_RESULT 0]   vwait TCL_RESULT}set serial [open
com1: r+]fconfigure $serial -mode "115200,n,8,1" -blocking 0 -buffering none# Give your modem some time, then read the answerset waitSecs   1set TCL_RESULT 0send_exp_reader $serial "AT\r" {OK} 5if {$TCL_RESULT==1} {    puts "Ok\n"} else { puts "Fail\
n"}close $serial
另外一個範例是 GPS Mouse Logger,處理的方法也是很類似,我們開啟一個 serial port 之後,再使用 fconfigure 來設定 baudrate 等參數。接下來使用 fileevent 等待事件的發生,當 serial port 可以讀(readable)或者是可以寫的時候,我們的動作就會繼續下去。

在讀取到資料之後,我們就使用 split 取出我們想要處理的資料,對於 Tcl 而言,串列的處理是他的強項,因此處理上也非常的簡單。

#!c:/tcl/bin/tclsh.exe############################################# Using serial port to read G-Mouse output## Author:  Danilo Danny Raynor# License: BSD License############################################proc private_reader {serial} {      global ori
g_log      global fixgps          set var ""     set record 0     set fix 0                set command_res [gets $serial]     if {[string first "\r" $command_res 0] != -1} {           after 1000     } else {         # parse the NMEA string and get dat
a            if {[string first {$GPGGA} $command_res 0] != -1} {           # parse our data from $GPGGA, get time, location and number of seatle        set data [split $command_res ","]       set timedata [lindex $data 1]       set y [lindex $data 2]
       set x [lindex $data 4]          set fix [lindex $data 6]                set snumber [lindex $data 7]                    if {$fix == 0 && $fixgps == 0} {              puts "GPS not ready... wait for GPS fix"            set fixgps 1
}                          if {$fix > 0 && $fixgps == 1} {           puts "GPS fix, ready to get data"           set fixgps 0              }               if {$fix > 0} {             puts "Time: $timedata, Position Y: $y, Position X: $x, satellites:
$snumber"           set record 1            } else {                    set record 0            }                                   } elseif {[string first {$GPRMC} $command_res 0] != -1} {          # parse our data from $GPRMC, get time, location and
speed of us           set data [split $command_res ","]           set timedata [lindex $data 1]           set y [lindex $data 3]          set x [lindex $data 5]              set use [lindex $data 2]                    set speed [lindex $data 7]
                          if {[string compare "A" $use]==0} {                puts "Time: $timedata, Position Y: $y, Position X: $x, Speed: $speed"           set record 2             } else {           set record 0             }
                 } elseif {[string first {$GPGSV} $command_res 0] != -1} {           set record 3        } elseif {[string first {$GPGLL} $command_res 0] != -1} {           set record 4        }                                             # record ou
r GPS message   if {$record != 0} {                 puts $orig_log $command_res                }    after 100            }       }# A flag to indicate GPS is fix or notset fixgps 0# Open a file to log our GPS messageset secondTime [clock format [clock
seconds] -format "%Y%d-%H%M%S"]set filename "orig_"append filename $secondTime ".txt"set orig_log [open $filename w]set END 0# user could set want to use what comport, from command line argumentset comport ""if {$argc > 0} {   set comport "com"   app
end comport [lindex $argv 0] ":"} else {   set comport {com6:}}set serial [open $comport r+]fconfigure $serial -mode "4800,n,8,1" -blocking 0 -buffering none# Write our initial stringputs -nonewline $serial "ASTRAL\r\n"fileevent $serial readable [list
private_reader $serial]vwait $ENDclose $orig_logclose $serial
Tcl Standard Library
Tcllib is a distribution of several packages for Tcl, all written entirely in Tcl, useful in a broad variety of areas.

Tcllib 包含了一個 delegation-based model 的物件系統(SNIT)和 C++ 物件系統相似但是比較簡化的物件系統(stooop),可以讓我們撰寫物件導向的程式。

--------------------------------------------------------------------------------

Tk
Tk 是一個很有用的跨平台、跨語言的使用者界面發展函式庫(一開始是 Tcl  的擴充套件, 但是現在已經被移植到其它的語言上供大家使用),有 buttons、menus、listboxes、 scrollbars 等等元件,有許多人就是為了使用這個可以快速開發 GUI 程式的擴充套件,才學 Tcl 的,
可以想見他的受歡迎程度,是 Tcl Language 的殺手級運用之一,雖然目前已經有更多的 GUI Framework 出現,但是 Tk 的易學易用仍然能夠讓他佔有一席之地。



--------------------------------------------------------------------------------

Expect
Expect 可以說是讓 Tcl 大受歡迎的套件之一, 可以說是 Tcl 的殺手級運用,提供外加的指令以控制互動行為的程式,可以做為軟體的測試、軟體自動化行程的設定、系統的監督等工作。


--------------------------------------------------------------------------------

TclX
TclX is oriented towards system programming tasks and large application development. It provides a variety of additional interfaces to the underlying operating system, as well as many new programming constructs, text manipulation tools, and debugging
capabilities.

Over the years, a variety of features originally appeared in TclX, and then, as the ideas were proven, migrated to the Tcl core. These features include Tcl's I/O system, its associative arrays, Internet networking interfaces, upvar, memory debugging..
. even incr.

TclX 支援下面幾類的命令:

General Commands
Debugging and Development Commands
Unix Access Commands
File Commands
Network Programming Support
File Scanning Commands
Math Commands
List Manipulation Commands
Keyed Lists
String and Character Manipulation Commands
XPG/3 Message Catalog Commands
Help Facility
Tcl Loadable Libraries and Packages
TclX 提供了一部份直接使用 System Call 的命令(如殺掉程序的 kill),以及提供跨平台的信號(signal)處理。

--------------------------------------------------------------------------------

BWidget
BWidget 提供一些已經組合好的 MetaWidgets(如 ScrolledWindow)給開發人員使用,讓我們能夠更快速的發展出我們的程式,目前是由 tcllib 維護。

--------------------------------------------------------------------------------

[Incr Tcl]

[Incr Tcl] 是一套以 C++/Java object model為基準的物件系統,同時也是最廣為人知、十分成熟的 Tcl 物件導向支援的擴充套件。

物件導向可以適度的讓程式發展的複雜度降低,[Incr Tcl] 提供了很棒的物件導向支援,可以讓程式發展人員在發展大型程式上能夠寫出更易懂的 code。

--------------------------------------------------------------------------------

[XOTcl]

Extended Object Tcl (for short: XOTcl, pronounced exotickle) is an object-oriented scripting language based on MIT's OTcl.


Tcl 的語法十分的簡單,而且非常的容易加以擴充,因此 Tcl 有眾多的物件導向支援的擴充套件,因此 Tcl 的煩惱不在於沒有物件系統,而在於他的選擇實在是太多了。


Tcl 預計將會在 8.5 支援物件導向,提供了一個強大的物件系統給大家使用,同時這個物件系統可以有效的支援目前的各個物件系統(例如 XOTcl 和 [Incr Tcl])。
--------------------------------------------------------------------------------

Img
這個套件提供了數種標準的圖形檔案格式,可以在 Tk 裡使用:

BMP
XBM
XPM
GIF (with transparency, but without LZW, due to patent restrictions)
PNG
JPEG
TIFF
postscript

--------------------------------------------------------------------------------

tCOM
This package has been incredibly helpful, provides both client and server COM programmability.


tCOM alse includes an implementation of a Tcl Active Scripting engine. one can do the following in Internet Explorer:

<html><head><title>TclScript Test</title><script language="TclScript">  proc setText {newValue} { text1 value $newValue }</script></head><body><input id="text1" name="text1" disabled="1"><p><input type="button" id="button1" name="button1" value="Hello
" onclick="setText hello"><input type="button" id="button2" name="button2" value="World" onclick="setText world"></body></html>
Since March 2002 this package is a part of the ActiveTcl Batteries Included distribution.
--------------------------------------------------------------------------------

Combat
Combat is an extension to the popular Tcl scripting language that allows accessing and providing CORBA objects at the script level, i.e. with Combat, your Tcl scripts can access CORBA services or become a CORBA server themselves.

Server-side CORBA scripting, which employs the object-oriented powers of [incr Tcl] also has its merits, as it can be done with much less code than a comparative C++ or Java server, without the need to recompile your code whenever some interface has c
hanged.



--------------------------------------------------------------------------------

Tcluno
Tcl and itcl interfaces for OpenOffice.org, as well as unospection, an introspection tool.


--------------------------------------------------------------------------------

Tcl/Java

Tcl Blend is a Tcl package that provides access to Java classes from Tcl. Tcl Blend is implemented using JNI. Jacl is an alternate implementation of Tcl 8.x, written entirely in Java.



--------------------------------------------------------------------------------

SQLite
SQLite 是一個內嵌式的 Database engine,提供了 Tcl Interface 可以讓 Tcl 直接使用,可以用來內嵌在 Tcl 應用程式內。
--------------------------------------------------------------------------------

NSTCL
nstcl is a Tcl package which reimplements many of the useful APIs and commands from AOLserver and OpenACS, making them available for use in Tcl/Tk applications & scripts. It is mature and powerful.

Most notable of these is the nstcl-database package, and the "ns_db" API, which provides a common interface to various different database extensions. As of version 1.0 nstcl now supports the following databases:


Oracle
PostgreSQL
Solid
Sybase
SQLite
MySQL
ODBC
如果可以,建議使用一些 common interface 的界面來發展  Database 的程式,在發展程式上比較簡單、同時也比較好維護。

下面是一些 nstcl 使用的範例:

#!C:/Tcl/bin/tclsh.exeset datasource localhost:5432:postgresset username usernameset password passwordpackage require nstclnamespace import nstcl::*#need to load Database packagepackage require Pgtclnstcl::load_driver "postgres"nstcl::configure_pool p
ostgres postgres 1 $datasource $username $password#set default poolnstcl::set_default_pool postgres

--------------------------------------------------------------------------------

FTP
Tcl 支援 server 和 client。
Tcllib 中有一個小型、簡單的 ftp server(ftpd):

#!c:/tcl/bin/tclsh.exe# Only for testpackage require logpackage require ftpdproc authUser {user password} { if {$user=="anonymous"} {  if {[string first "@" $password] > 0} { return 1; } }  return 0;}proc authFile {user path access} {  return 1;}set :
:ftpd::cwd [pwd]set ::ftpd::welcome "Welcome to TclFtpd"::ftpd::config -authUsrCmd authUser -authFileCmd authFile::ftpd::server vwait forever

--------------------------------------------------------------------------------

CGI 和動態網頁
Tcl 可以用來寫 CGI,而且因為 Tcl 對於字串有著良好的處理能力,配合 Tcl 廣大的套件,讓 Tcl 在撰寫 CGI程式非常的方便。

下面是 Tcl 版的 Hello World(在 Windows 平台上執行的):
#!c:/Tcl/bin/tclsh.exeputs "Content-type: text/plain\r\n"puts "Hello World\r\n"
因此,我們可以印出環境變數,如下例:

#!c:/Tcl/bin/tclsh.exeputs "Content-type: text/plain\r\n"parray env
功能強大的 Tcllib 也包含了幾個模組可以讓大家寫 cgi 程式,包含了:


ncgi
html
htmlparse
javascript
下面是印出環境變數的例子:

#!c:/Tcl/bin/tclsh.exepackage require ncgipackage require html::html::init::ncgi::header    set title "Print Environment"puts [::html::head $title]puts [::html::bodyTag]puts [::html::tableFromArray env]puts [::html::end]
因為 CGI 執行的時候都需要啟動一個 Process,所以有人在這方面做了改進,詳情可以參考 FastCGI。

Apache.org 目前 Apache Tcl 有幾項幾於 Tcl 的計劃:

mod_tcl
Apache Rivet, Apache Rivet 主要是發展類似 PHP 這種可以內嵌在 Html 中,但是語法是使用 Tcl 的解決方案,但是目前只能運行在 Apache 1.3 上。
Websh
著名的 AOL Server(可以處理 28,000 hits/sec 的優良Web Server,運作在 FreeBSD, Linux, Solaris, and Mac OS/X 10.2 上),內建了自己的 script language 和提供 pool 的方式與資料庫進行連結。AOL Server 所選用的語言,就是 Tcl,而且連設定檔都是使用 Tcl,讓 Tcl
User 使用者根本不用花什麼力氣學習就可以輕鬆的使用。AOL Server本身有支援一些資料庫。這些資料庫分別是 Interbase, MySQL,Oracle, Postgres, SOLID幾個主要 Unix上的資料庫,AOL Server都已經有支援。

AOL Server 對於想要擁有超高穩定度,高效能,高擴充性Web Server的人而言,是一個非常具有吸引力的軟體,而對於 Tcl User 來說,更是一個極好的軟體。


Thttpd 是一個用 Tcl 實作,有著高效能的 Http Server,可以單獨使用,也可以內嵌到其它的程式中使用。
--------------------------------------------------------------------------------

GPIB 的支援
GPIB-TCL是一個 TCL 的 extension,新增一個 gpib 命令讓你可以透過 industry-standard IEEE-488 (GPIB) 與測量儀器溝通。


安裝
在 Tcl lib 的目錄下面,建立一個 gpib 的子目錄。再來,複製 gpib.dll 和 pkgindex.tcl 到剛才建立的 gpib 目錄。
接下來,使用 tclsh,


package require gpib
你將會看到一個版本回應。


再來打 gpib,會看到有關於這個命令的描述,這樣就安裝成功了。

--------------------------------------------------------------------------------

Windows platform:控制台
rundll32.exe 是個有趣的程式,可以直接呼叫 dll 中的函式,在 Windows 平台中非常的好用。我們可以透過 rundll32.exe 來啟動控制台元件,如下面所展示的,呼叫「新增/移除程式」元件,

exec rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl
當你必須在自己的程式中叫用控制台元件時,只要這樣呼叫就可以了。
--------------------------------------
Embedded Tcl
下面是一個簡單的範例,讀取一個 Tcl script 並且加以執行。
bool ExecuteScript(const char *program, const char *scriptfile){ Tcl_Interp *tcl_interp;  int rc ; Tcl_FindExecutable(program); tcl_interp = Tcl_CreateInterp(); Tcl_AllowExceptions(tcl_interp); if ( tcl_interp == NULL ) { return false; /// execute fai
l. }  if ( Tcl_Init(tcl_interp) != TCL_OK ) {   return false; }  rc = Tcl_EvalFile(tcl_interp, scriptfile) ;  Tcl_DeleteInterp(tcl_interp); if ( rc != TCL_OK ) {   return false; /// execute script fail. } return true;}

--------------------------------------------------------------------------------

擴展 Tcl
我們可以使用 C/C++/Java 等來為 Tcl 撰寫 Extension Package。

下面是一個初始化的例子:

int Control_Init(Tcl_Interp* interp){#ifdef USE_TCL_STUBS    Tcl_InitStubs(interp, " 8.4", 0);#endif    Tcl_Obj *version = Tcl_SetVar2Ex(interp, "control_version", NULL,                                     Tcl_NewStringObj("0.1", 8), TCL_LEAVE_ERR_MSG)
;    if (version == NULL)        return TCL_ERROR;            Tclcontrol_InitHashTable();                int r = Tcl_PkgProvide(interp, "Control", Tcl_GetString(version));         Tcl_CreateObjCommand(interp, "GetSystemDirectory", CONTROL_GetSystemDir
ectory, (ClientData) NULL, NULL);               return r;}
實作 command 的 function 則可以這樣實作:

int CONTROL_GetSystemDirectory(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST obj[])){ Tcl_Obj *returnvalue;   char lpBuffer[145]; unsigned int size = 144;            if(objc != 1)   {       Tcl_WrongNumArgs(interp, 1, obj, " ");
   return TCL_ERROR;   }           GetSystemDirectory(lpBuffer, size); returnvalue = Tcl_NewStringObj(lpBuffer, -1 );              Tcl_SetObjResult(interp, returnvalue);  return TCL_OK;}

--------------------------------------------------------------------------------

Tclkit
Tclkit 是一個包含 Tcl, Tk, IncrTcl, TclVFS, 和 database (Metakit) 套件的單一可執行檔。

Tclkit is a single file that acts as 100% complete runtime for Tcl/Tk without requiring any installation:

install == cp/copy
uninstall == rm/del
我們可以使用 SDX 來包裝我們的 tcl script 為一個單一的檔案(稱之為 "file system in a file",這是 Tcl VFS 的強大功能所帶來的技術,可以用 Tclkit 來執行經過包裝後的檔案),是個很有趣的技術。
--------------------------------------------------------------------------------

下載
在 Windows 平台上,目前 ActiveState 有整理好的 binary 檔案、Tcl 標準函式庫和一些擴充套件,提供你高品質的程式,可以直接下載使用。


--------------------------------------------------------------------------------
Tcl  中文網:簡體中文資料最豐富的網站
FreeWarp:包裝你的 Tcl script 變成一個單獨的可執行檔
Tcl for Web Nerds:一個很優秀的 Tcl 教學網站
Tcl Scripting
The Tcler's Wiki
Tcl Developer Site
Tcl the Misunderstood
Reply all
Reply to author
Forward
0 new messages