Tcl简介
Tcl是一种非常简单的编程语言,如果你以前曾经学过编程,那么你只要学习几个小时就可以编写出一些有趣的Tcl程序。本文将对Tcl的功能做一个大概的 介绍。一般来说,读完本文之后,你就可以开始独立的编写一些简单的Tcl脚本了;不过,要想获得更全面的认识,我们建议你还是去参考几本目前已经出版的 Tcl书籍。
基本语法
Tcl脚本由一些被换行符或是分号分开的命令所组成。命令都有相同的基本格式,如下面的例子所示: expr 20 + 10
该命令计算20加10的和,并返回结果30。你可以把这个例子以及本文中的所有其它的例子键入到tclsh这样的Tcl应用程序中来验证它们;在一个命令结束后,tclsh将打印出它的结果。
每个Tcl命令都含有一个或多个被空格分开的单词,在这个例子中有4个单词:expr,20,+,和10。第一个单词是一个命令名,其余的单词是这个命令 的参数。所有的Tcl命令都含有一些单词,但不同的命令对他们的参数有不同的处理方式。expr命令把它的所有参数看作是一个算术表达式,计算表达式的结 果,并以字符串的形式返回结果。在expr命令中,单词之间的分隔不是很重要:同样的命令你可以写成这种形式: expr 20+10
不过,对大部分的命令来说,单词的结构是很重要的。每个单词都会用于不同的目的。所有的Tcl命令都返回结果。如果一个命令产生了没有意义的结果,那么它将返回一个空字符串作为它的结果。
变量
Tcl允许你在变量中保存数值,并且可以在后续的命令中使用这些数值。set命令用于对变量进行读写操作。比如,下面的命令对变量x赋值为32。 set x 32
这个命令返回变量的新值。你可以让set只带一个参数来读出变量的数值: set x
你不需要在Tcl中声明变量:变量在第一次set的时候被自动创建。Tcl变量没有类型:任何值可以赋给任何变量。
要想在一个命令中使用变量的值,可以采用变量替代,如下例所示: expr $x*3
当一个字符$出现在一个命令中的时候,Tcl把跟在它后面的字母和数字看作是
一个变量名并且将其替换成变量的值。在这个例子中,expr命令接收到的实际 参数将是32*3(假设变量x在前面的例子中被set过)。你可以在任何命令的任何单词中使用变量替代,甚至对一个单词多次使用: set cmd expr set x 11 $cmd $x*$x
命令替代
你也可以在一个命令的参数中使用另一个命令的结果。这被称之为命令替代: set a 44
set b [expr $a*4]
当中括号出现在一个命令中的时候,Tcl把中括号之间的所有语句看作是一组Tcl命令。Tcl对这组命令进行解释,并用结果替代中括号之间的文字。上面的例子中,第二个set命令的第二个参数将为176。
双引号和大括号
双引号允许你指定包含空格的单词。我们看下面的这个例子: set x 24 set y 18
set z \"$x + $y is [expr $x + $y]\"
在这三个命令都被解释后,变量z的值将是24+18 is 42。双引号之间的所有语句作为一个单词传给set命令。此处需要注意几点(a)引号之间的命令和变量替代仍起作用,(b)引号自身不会被传给命令。如果 没有引号的话,set命令会得到6个参数,这将引起错误。
花括号对单词提供另外一种组合信息的方式。它与双引号的不同之处在于:花括号内的替代不起作用:
set z {$x + $y is [expr $x +$y]}
这个命令把变量z赋值为 \" $x + $y is [expr $x +$y]\"。
控制结构
Tcl提供一整套控制结构包括条件,循环和过程。Tcl的控制结构只是一些将Tcl脚本作为参数的命令。下面的示例创建了一个叫做power的Tcl的过程,实现对一个数求它的n次方: proc power {base p}{ set result 1 while {$p >0}{
set result [expr $result * $base] set p [expr $p- 1] }
return $result }
这个脚本由一个简单的命令proc所组成,这个proc命令有3个参数:过程名,参数名列表和过程体。过程体是一个Tcl脚本。需要注意的是,第一行末尾 的花括号和最后一行花括号之间的语句都被逐字逐句的作为一个参数传给proc。proc命令创建了一个新的叫做power的具有两个参数的Tcl命令。你 可以这样来调用power: power 2 6 power 1.15 5
当power被调用的时候,过程体就被解释了。当过程体执行的时候,它可以变量的形式进入它的参数中:base获得第一个参数,p获得第二个参数。
power过程体中包含3个Tcl命令:set,while和return。while命令完成了这个过程中的大部分工作。它有两个参数,一个表达式($ p>0)和一个Tcl脚本写的主体。while命令使用与C语言相似的规则来解释它的表达式参数。如果结果是真(非零),那么它将把函数体作为脚本 来执行。他不断的重复这个过程,直到最后表达式的值变为假(零)。在这个例子中,while命令的函数体将result的值与base相乘,然后减少p的 值。当p的值减到零的时候,结果就包含了需要的base的n次方值。return命令的作用是退出过程,同时将变量result的值作为过程的结果返回。
命令是如何运作的?
正如你所见到的,Tcl中所有有趣的功能都是靠命令来描述的。声明是命令,表达式由可执行命令来解释,控制结构是命令,过程也是命令。
Tcl命令由3种方式来创建。一组命令由Tcl解释器自身来提供。这些命令称之为内建命令,他们包括到现在为止你已经看到的命令和更多其他的命令(见下面)。内建命令出现在所有的Tcl应用程序中。 第二组命令使用Tcl扩展机制来创建。Tcl提供许多API接口,允许你用C或C++编写一个命令过程来创建一个新的命令。然后你可以通过Tcl解释器来 通知Tcl过程实现的命令名,来注册命令过程。以后只要那个特定的名称被当作Tcl命令来使用,Tcl就会调用你的命令过程来执行这个命令。其实内建命令 也是使用同样的扩展机制来执行的,只不过它们的命令过程是Tcl库中的一部分。 当Tcl用于嵌入到一个应用程序中的时候,应用程序使用扩展机制把它的主要功能和Tcl结合起来,这样一来那些可用的Tcl命令就因程序的不同而不同。还 有大量的扩展包可以和Tcl应用程序组合起来使用。Tk是最著名的扩展之一,它提供了许多强大的创建图形用户界面的工具。而其他的扩展则提供了面向对象的 编程,数据库访问,更多的图形功能,及其他的一些特性。用Tcl来创建完整的应用程序的优势之一是它可以很方便的进行扩展,从而可以组合进新的功能或与其 它的资源进行通信。
第三组命令是一些由proc命令创建的过程。比如上面创建的power命令。通常,扩展用于较低层的功能,用C语言实现比较方便;而过程用于较高层的功能,用Tcl实现比较方便。
其它的功能
Tcl除了前面的例子中所使用的命令外,还有许多其它的命令,这里举例给出一
些内建的Tcl命令所具有的功能:
☆ 更多的控制结构,如if,for,foreach和switch
☆ 字符串操作,包括一个强大的规则表达式匹配工具。不定长字符串可以像数字一样被传递和操作
☆ I/O 输入输出包括磁盘,网络通信通道,和类似串口的设备中的文件。Tcl为在Internet上进行通信提供了非常方便的工具
☆ 文件管理:Tcl提供许多命令来操作文件名,读写文件属性,复制文件,删除文件,创建目录,等等 ☆ 分支过程的实现:你可以使用exec命令来运行其他程序,并且可以在它们的运行过程中和它们进行通信
☆ Lists Tcl使创建数值的集合变得很容易,并且可以用许多方式来对它们进行操作
☆ 数组 你可以创建有成对的名称数值组成的结构化数值,同时可以使用不定长字符串来定义这些名称和数值 ☆ 可以对时间和日期进行操作
☆ Events允许等待一定的事件发生后再触发Tcl脚本,比如一段时间的延迟或是网络通信中输入的数据变为有效
Expect简介
一个叫做fsck的Unix文件系统检查程序,可以从Shell里面用-y或者-n选项来执行。 在手册[1]里面,-y选项的定义是象这样的。
“对于fsck的所有问题都假定一个“yes”响应;在这样使用的时候,必须特别的小心,因为它实际上允许程序无条件的继续运行,即使是遇到了一些非常严重的错误”
相比之下,-n选项就安全的多,但它实际上几乎一点用都没有。这种接口非常的糟糕,但是却有许多的程序都是这种风格。 文件传输程序ftp有一个选项可以禁止交互式的提问,以便能从一个脚本里面运行。但一旦发生了错误,它没有提供的处理措施。
Expect是一个控制交互式程序的工具。他解决了fsck的问题,用非交互的方式实现了所有交互式的功能。Expect不是特别为fsck设计的,它也能进行类似ftp的出错处理。
fsck和ftp的问题向我们展示了象sh,csh和别的一些shell提供的用户接口的局限性。 Shell没有提供从一个程序读和象一个程序写的功 能。这意味着shell可以运行fsck但只能以牺牲一部分fsck的灵活性做代价。有一些程序根本就不能被执行。比如说,如果没有一个用户接口交互式的 提供输入,就没法运行下去。其他还有象Telnet,crypt,su,rlogin等程序无法在shell脚本里面自动执行。还有很多其他的应用程序在 设计是也是要求用户输入的。
Expect被设计成专门针和交互式程序的交互。一个Expect程序员可以 写一个脚本来描述程序和用户的对话。接着Expect程序可以非交互的运行“交互式”的程序。写交互式程序的脚本和写非交互式程序的脚本一样简单。 Expect还可以用于对对话的一部分进行自动化,因为程序的控制可以在键盘和脚本之间进行切换。
Tcl安装配置
1. 安装ActiveTcl 8.4(包括Expect扩展) 下载地址: http://downloads.activestate.com/ActiveTcl/Windows/8.4.15/ActiveTcl8.4.15.0.280619-win32-ix86-threaded.exe 2. 下载twapi 下载地址:http://jaist.dl.sourceforge.net/sourceforge/twapi/twapi-1.1.5.zip 安装:解压缩到ActiveTcl 8.4安装目录下的lib文件夹。
使用tcl,Expect在VC环境测试C/C++函数
1. 利用Visual C++建立一个控制台程序的工程,工程的名字为TclTest。 2. 把下面代码拷贝到TclTest.h #include \"tcl.h\" #define RET_ERR false #define RET_OK true #define MAX_WORD_LEN 32 int GetWordFromStr(const char* pStrSource,char * pDestStr,int len); //这里更改为你好测试的函数定义。 int Tcl_EXGetWord(ClientData clientData, Tcl_Interp * interp, int argc, char* argv[]); int Tcl_AppInit(Tcl_Interp *interp); 3. 把下面代码拷贝到TclTest.cpp #include \"stdafx.h\" #include \"TclTest.h\" #include \"memory.h\" //这里更改为你好测试的函数实现。 int GetWordFromStr(const char* pstrSource, char* pstrDest, int len) { if (pstrSource == NULL || pstrDest == NULL || len < 0) return false; strncpy(pstrDest, pstrSource, len);
return true; } /*GetWordFromStr函数的扩展指令函数,依据测试函数的参数不同,修改函数的内部*/ int Tcl_EXGetWord(ClientData clientData, Tcl_Interp * interp, int argc, char* argv[]) { int iPos; bool rCode; char pDestStr[32]; memset(pDestStr,0,32); if (TCL_OK != Tcl_GetInt(interp,argv[2],&iPos)) { printf(\"the para 2 is wrong\\n\"); return TCL_OK; } rCode = GetWordFromStr(argv[1],pDestStr,iPos); If(rCode == RET_OK) { printf(\"The Destine string is %s\\n\ } else { printf(\"It's fail to get the string!\\n\"); } return TCL_OK; } /*tcl解释器和扩展指令的初始化函数*/ Int Tcl_AppInit(Tcl_Interp *interp) { /*创建interp解释器可以识别的扩展指令,指令的名字是GetWord(依据需要修改),执行该指令, 直接调用Tcl_EXGetWord扩展指令函数,通过传递参数,可以执行单元测试用例*/ Tcl_CreateCommand(interp, \"GetWord\(Tcl_CmdProc *)Tcl_EXGetWord, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); return TCL_OK;
}
4. 添加tcl头文件和库文件,并设置相应的头文件和库文件路径 头文件路径:ActiveTcl 8.4安装目录/lib/ tcl8.4 库文件路径:ActiveTcl 8.4安装目录/bin
5. 编译通过后,运行,出现控制台程序,输入GetWord “wo ai zhong guo” 3,则出现如下
的结果界面。
使用twapi测试windows应用程序
twapi介绍 The Tcl Windows API (TWAPI) extension provides Tcl bindings to almost 450 functions in the Windows API. The extension provides access to the Windows API at two levels. A direct interface to the supported Windows API is provided where the Tcl commands directly map to Windows functions as described in Microsoft Windows SDK. The recommended interface is a higher level interface that is more convenient, powerful and easier to use than the Windows API. Example 1
功能:给系统添加快捷键。
package require twapi
set notepad_hk \"Ctrl-Alt-F11\" set exit_hk \"Ctrl-Alt-F12\"
puts \"$notepad_hk will bring up a new copy of notepad\" puts \"$exit_hk will unregister the hotkey and exit\"
proc remove_hotkeys_and_exit {} {
twapi::unregister_hotkey $::notepad_hk_id twapi::unregister_hotkey $::exit_hk_id exit }
set notepad_hk_id [twapi::register_hotkey Ctrl-Alt-F11 \"exec notepad.exe &\"]
set exit_hk_id [twapi::register_hotkey Ctrl-Alt-F12 \"remove_hotkeys_and_exit\"]
# If running in tclsh, need a vwait to get eventloop running. # Comment out the line if in wish vwait forever
Example 2
功能:向指定程序发送文本。
package require twapi
if {$argc != 2} { puts stderr \"Usage: [info nameofexecutable] $argv0 WINDOWTITLE TEXT\" exit 1 }
set title [lindex $argv 0] set data [lindex $argv 1]
# Get all windows with that title
set windows [twapi::find_windows -text $title] if {[llength $windows]} {
set win [lindex $windows 0] # Set the focus to the window twapi::set_focus $win
# Feed data in to the input queue twapi::send_input_text $data } else {
puts stderr \"No windows found with title '$title'\" }
Example 3
功能:在指定程序上模拟使用鼠标。
package require twapi
if {$argc != 1} {
puts stderr \"Usage: [info nameofexecutable] $argv0 WINDOWTITLE\" exit 1
}
set title [lindex $argv 0]
# Get all windows with that title
set windows [twapi::find_windows -text $title] if {[llength $windows]} {
set win [lindex $windows 0]
# Move the window to the foreground twapi::set_foreground_window $win
# Get top right corner of the window
foreach {x y} [twapi::get_window_coordinates $win] break
# Move the mouse there (plus a small delta to get to the menu) twapi::move_mouse [incr x 10] [incr y 10]
# Click the left mouse button twapi::click_mouse_button left
} else {
puts stderr \"No windows found with title '$title'\" }
存在的问题:
1. 使用这种方法测试的程序必须使用windows api编写,使用其它类库编写的部分不能进 行测试。
2. 使用这种方法测试图形界面程序的方法,类似开发外挂程序,受很多不确定因素影响。
因篇幅问题不能全部显示,请点此查看更多更全内容