当前位置:文档之家› TCL脚本语言-12-程序库和程序包

TCL脚本语言-12-程序库和程序包


auto_load:加载程序库
auto_load 命令是在 init.tcl 中定义,在解释器初始化的时候被创建。当解释器碰到了未 定义命令的时候,unknown 命令会被调用,在 unknown 中会调用 auto_load 命令来寻找这个
作者:雷雨后
Email: leiyuhou010@
1. 每一个 TCL 应用程序都应该有一个 init.tcl 文件,该文件会做一些初始化的工作, 这个文件都位于 info library 命令所返回的目录中。
2. unknown 命令就是在这个 init.tcl 文件中定义的。 例如我现在使用的 ActiveTcl,运行 info library 命令,返回解释器的库目录为:
source:TCL 中的#include
C/C++中使用库文件的方式,首先必须在源程序中#include 相关的头文件,编译通过后, 在链接(link)阶段与相关的库文件(.lib、.obj 等)连接。在 TCL 中则没有所谓头文件和库 文件,那么分散在其他多个文件的命令和程序,如何被我们的应用中加载引用呢?有一个命 令可以完成类似#include 的功能,这就是 source。其命令语法如下:
未知命令是否是 DOS 内部命令或者是某一个可执行程序。如果是,则执行它 们,并且返回执行结果。 b) 如果没有找到可执行命令文件,或者定义了 auto_noexec,那么就判断该未知 命令是否是“!!”、“!(.+)$”等这样的格式;如果是,那么就调用对应的历史 命令。 6. 如果不是交互模式,则抛出异常,退出 unknown。 上面是 unknown 命令的默认处理流程,我们可以自己重新编写这个命令,但是最好要 保留原来的功能。因为其处理机制非常重要,保证了 TCL 程序库中定义的命令能够被自动 的加载到 TCL 解释器中。完成这个功能的就是 auto_load 命令。
# Tcl autoload index file, version 2.0 # This file is generated by the "auto_mkindex" command # and sourced to set up indexing information for one or # more commands. Typically each line is a command that # sets an element in the auto_index array, where the # element name is the name of a command and the value is # a script that loads the command.
unknow 方法
请先思考下面的问题:Windows 操作系统下,当我们执行命令 tclsh 启动一个 TCL 解释 器之后,进入交互模式,然后在 TCL 的命令提示符下面输入 dir 命令,会怎样?得出结论后, 然后动动手实践一下来验证你的结论是否正确。
创建一个文件 test.tcl,其内容只有一行,就是 dir 命令,然后在命令提示符下面通过输 入命令行 tclsh test.tcl 来执行这个 TCL 脚本,看看结果怎么样。
% set auto_path
作者:雷雨后
leiyuhou010@
TCL、Python 和软件测试自动化
144
C:/Tcl/lib/tcl8.4 C:/Tcl/lib
上面是 auto_index 和 auto_path 两个变量的内容。在 C:/tcl/lib/tcl8.4 目录下存在一个文件 tclIndex,其内容如下:
source filename 唯一的参数是文件名,source 命令读取文件全部内容,然后交给 TCL 解释器来执行, source 命令的结果就是文件中最后一个命令的结果。如果执行的时候发生了异常,source 命 令也会抛出异常。 source 命令在 library 和 package 两种模式中都得到了广泛的应用。
set auto_index(auto_reset) [list source [file join $dir auto.tcl]] set auto_index(tcl_findLibrary) [list source [file join $dir auto.tcl]] set auto_index(auto_mkindex) [list source [file join $dir auto.tcl]] set auto_index(auto_mkindex_old) [list source [file join $dir auto.tcl]] „„
化、执行 init.tcl 文件的时候被定义并且初始化,过程如下: a) 如果有环境变量 TCLLIBPATH,其值将是 auto_path 的第一个元素; b) 命令 info library 所返回的路径以及其父目录会加入到 auto_path 中; c) 当前可执行文件所在目录的父目录下的 lib 子目录,会加入 auto_path 中; d) 如果定义了变量 tcl_pkgPath,其中每一个元素也会加入到 auto_path 中。 可见我们可以将库文件放到很多分散的目录中,只要在 auto_path 变量中有这些目 录中就行了。 2. auto_index:一个数组变量。其中每一个元素的下标是命令名字,对应值则是一条 TCL 语句。通过执行该语句能够定义对应的命令。例如:元素 parray 对应的语句 是“source C:/Tcl/lib/tcl8.4/parray.tcl”。执行该语句后,parray 命令就会被定义。一 般而言,这个语句类似 source xxxfile 的格式。过程就是在 xxxfile 中被定义。 auto_load 的语法格式如下:auto_load cmd ?namespace?;其中的 cmd 就是解释器碰到的 未知命令,当它被调用的时候: 1. 判断数组 auto_index 中是否存在下标为 cmd 的元素。如果有则执行对应的语句,然 后判断命令 cmd 是否被定义;如果存在就返回 1,表示加载成功; 2. 接着判断 auto_path 变量是否存在,如果不存在就返回 0,表示加载失败; 3. 然后依次在 auto_path 所包含的每一个目录下,寻找名字为“tclIndex”的文件,判 断这个文件是否是有效文件。如果有效,那么就执行这个文件。 4. 然后再次判断 auto_index 中是否存在下标为 cmd 的元素,如果存在,则执行对应的 语句,然后判断命令 cmd 是否存在(是否被定义了);如果存在,就返回 1。 5. 最后返回 0,表示自动加载失败。 这就是 auto_load 命令的全部执行过程。可以看到,除了前面介绍的 auto_path 和 auto_index 变量之外,还涉及到了一个文件 tclIndex。这个文件的内容很简单:就是用来设 置 auto_index 变量的值。例如在我的系统上:
TCL、Python 和软件测试自动化
143
未定义命令。弄清楚 auto_load 的执行机制,会让我们更加清楚 TCL 程序库(library)的工 作机制和自动加载的原理。
首先得介绍一下和 aoto_load 相关的两个全局变量: 1. auto_path:列表变量,其每一个元素是库文件所在的目录。这个变量在解释器初始
作者:雷雨后
Email: leiyuhou010@
TCL、Python 和软件测试自动化
142
里面就不能执行了呢?其实,这都是 unknow 命令在作怪。 实际上,TCL 解释器在解释执行的时候,如果碰上了当前不认识的命令名字,都会调用
一个命令:unkonwn;并且把这个未知命令连同其参数作为 unknow 命令的参数。unknown 命令一般是在 TCL 解释器初始化的时候来定义的。作为一个比较普遍的规则:
行该命令,然后返回; 4. 如果没有定义全局变量 auto_noload,则调用 auto_load 命令来尝试从程序库中加载
和寻找这个未知命令;如果找到则执行它,返回结果; 5. 如果 auto_load 没有找到该命令的实现,那么就判断是否是处在交互模式下;如果
是交互模式: a) 并且没有定义全局变量 auto_noexec,那么就调用 auto_execok 命令来判断这个
结果比较让人诧异:交互模式下,dir 命令能够执行,并且列出了当前目录下的所有内 容,和一般情况下执行 dir 的结果并无差异;但是执行脚本,则报告了一个错误:invalid command name “dir”。怎么回事?dir 是标准的 DOS 内部命令,它不属于 Tcl 的命令。这样 的命令按照常理是不能够被执行的,但是事实上在交互模式下却正确执行了,那为何在脚本
里面就是一堆的 set auto_index(cmd)这样的命令。可以看到,第一条 auto_reset 命令可以 通过执行“source auto.tcl”语句来被定义。根据我们前面对 unknown 和 auto_load 命令的分 析,如果解释器碰到了 auto_reset 命令并且该命令没有被定义,那么就会通过 unknown 和 auto_load 来执行 source auto.tcl,而 auto_reset 命令就是在文件 auto.tcl 中被定义。所以 auto_load 能够加载成功,并且在 unknown 中会执行这个 auto_reset 命令。如果再次调用 auto_reset 命 令,就不会再次进行同样的加载了,因为这个命令已经被定义了,可以被直接调用。
% foreach {k v} [array get auto_index] {puts "$k = $v"} tcl_startOfNextWord = source C:/Tcl/lib/tcl8.4/word.tcl parray = source C:/Tcl/lib/tcl8.4/parray.tcl pkg_mkIndex = source C:/Tcl/lib/tcl8.4/package.tcl ::safe::Lappend = source C:/Tcl/lib/tcl8.4/safe.tcl history = source C:/Tcl/lib/tcl8.4/history.tcl ::safe::AliasSubset = source C:/Tcl/lib/tcl8.4/safe.tcl „„
相关主题