在上一篇文章“Tcl脚本初步学习”中,介绍了Tcl脚本的一些基本语法,本文将继续学习一些常用的命令。
1.列表
列表是元素的有序集合,各个元素可以是任何字符串,每个元素由空格、制表符或换行符隔开,在使用时通常用花括号将列表中的所有元素括起来。
以下通过一些示例来展示列表的一些用法:
set x {a b c}
这条命令创建一个列表x,x中的元素有a、b、c
set y [list {a b c} {d e} f {g h i}]
list命令把参数加入到列表元素中,这里创建一个列表y,有4个元素
lindex $y 0 2
取出列表中的第1个元素{a b c}中的第3个元素c,第一个元素的地址是0
llength $y
计算列表y的元素个数,当前为4
concat {a b c} {d e} f {g h i}
把子列表中的元素全部取出来组成一个大列表a b c d e f g h i
lappend x d
向列表后面添加元素d
2.过程
在Tcl中,过程相当于C语言中的一个函数,过程以proc命令开始,接着分别是过程名、参数列表和由Tcl脚本组成的过程块。
下面举一个例子:
proc ifcapable {expr code {else ""} {elsecode ""}} {
set e2 [fix_ifcapable_expr $expr]
if ($e2) {
set c [catch {uplevel 1 $code} r]
} else {
set c [catch {uplevel 1 $elsecode} r]
}
return -code $c $r
}
在这个过程中,过程名是ifcapable,参数列表是{expr code {else ""} {elsecode""}},这里有4个参数分别是expr、code、else和elsecode。为什么参数else和elsecode要用花括号括起来来呢?这里的意思是如果else和elsecode参数不存在,那么默认用双引号中的内容替换,此处用空白替换。
在语句set c [catch {uplevel 1 $code} r]中,catch命令表示执行uplevel 1 $code命令时返回一个布尔变量,成功了返回0,如果有错误出现,错误信息会保存到r中。在这里传入的code参数应该是一个可执行的脚本,uplevel 1表示调用堆栈的上一层中的变量对当前的code脚本是可见的,否则code中的变量都会被当作局部变量处理。
在语句return -code $c $r 中,-code是return命令的参数选项,他会返回指定状态,这里用$c表示异常代号,通常没有错误时为0,而$r是该过程的返回值。
3.文件
大部分处理文件的命令以file开头,下面举例说明
file tail a/f.c
该命令提取文件路径a/f.c中的文件名f.c
file dirname a/f.c
提取文件所在的目录a
file split b/a/f.c
将文件分割成3个部分b a f.c
file join b a f.c
将b a f.c这3个元素组合成一个文件路径b/a/f.c
file mkdir foo
创建一个工作目录foo
file delete {*} [glob *.tmp]
这条语句删除了所有后缀名为tmp的文件,注意file delete *.tmp仅仅是删除一个名为*.tmp的文件
file copy a.txt b.txt
把文件b.txt的内容复制到a.txt,如果文件b.txt已经存在会返回错误,如果要覆盖使用-force参数
file copy a.txt b.txt Document
这里Document是一个目录名,这条语句把a.txt b.txt文件复制到该目录下
另外还有很多与文件相关的命令就不一一介绍了,下面主要来了解一下怎么读写文件I/O的。
set fd [open a.txt w]
puts $fd abc
close $fd
打开一个文件a.txt,w表示可写,向文件中写入abc
set fd [open a.txt r]
gets $fd
close $fd
打开一个可读的文件,用gets命令读取文件中的一行数据
4.进程和管道
exec创建一个或多个子进程,并且等待进程完成并返回,exec的第一个参数是程序名,后面为传递给该程序的参数,例如
exec rm main.o
该命令打开一个rm进程,传递给rm的参数是main.o。
打开一个管道(pipe)和打开一个文件一样都是使用open命令,不同的地方在于打开管道时传给open的第一个字符是管道符号|,后面的参数和exec参数的意义相同,也是打开一个子进程管线,并返回一个描述符。
管道和文件的不同之处在于管道本身也是一个进程,管道和进程通信时相当于相互收发数据,管道里自己有程序可以自己处理收到的数据并决定要发送什么数据和进程。而文件里只是保存数据,数据由进程读写,文件本身不会对数据进行操作。
set prg [info nameofexec]
set chan [open "|$prg tf_main.tcl" r+]
fconfigure $chan -buffering line
fconfigure $chan -blocking 1
上述代码中set prg [info nameofexec]把prg设为当前进程名,即一个Tcl解释器,[open "|$prg tf_main.tcl" r+]以可读写的方式打开一个管道,这个管道是一个Tcl解释器执行一个tf_main.tcl脚本,打开后管道描述符存在chan变量里。
fconfigure命令对管道chan配置参数,这是老的写法,最新的Tcl用chan configure命令来代替fconfigure。上述代码配置管道缓存为每一行输出一次,读取数据为阻塞模式。
打开管道后就可以使用puts和gets命令和管道通信了,例如
puts $chan $cmd
puts $chan OVER
以上命令向管道输入数据$cmd,OVER,$cmd可以是一段脚本,管道收到后可以执行该脚本,管道收到OVER表示接收结束。这就是管道的作用,当前进程可以通过管道建立一个子进程,然后再把数据发送给子进程,由子进程来执行。
set line [gets $chan]
上述命令表示向管道取出一行数据并放在line变量里。
而在管道中分别通过stdin和stdout来接收和发送数据
gets stdin
puts $result
puts OVER
puts命令没有指明管道,默认往stdout发数据。这里有一个问题,管道向主进程通过stdout发数据,但是如果此时管道执行脚本时出现printf之类的函数也会向stdout发数据,此时主进程也会收到打印的信息,所以通信前需要制定好通信协议。
5.数组
数组由数组名和索引组成,索引由()括起来,索引不一定是数字,如下代码创建一个earnings数组,数组中有2个元素
set earnings(January) 87966
set earnings(February) 9540
arraysize用来确定数数组元素的个数,array name则返回数组元素所有索引的列表。
array size earnings
返回2
array name earnings
返回January February
数组可以和foreach命令一起使用
foreach i [array name earnings] {
puts $earnings($i)
}
返回87966 9540
6.其他命令
info exists varName
如果名为varName的变量在当前上下文(作为全局或局部变量)存在,返回1,否则返回0。
eval script
执行一段script的脚本
source file
导入一个Tcl脚本文件并执行
string index
用来从一个字符串中取出对应字符,如
string index " abcde" 3
取出的字符穿的第3个字符d,注意索引从0开始