Tcl语法概要
Tcl的语法规则算是比较简单的。简单来说,可以用下面几条规则来归纳:
- Tcl脚本由命令序列组成。
- 命令以换行符或者分号
;
结束。 - 解析器每次只解析一条命令,然后执行这条命令。
- 命令以换行符或者分号
- Tcl命令由“单词”(Word) 序列组成
- 单词之间通过空白字符(空格,Tab)分隔
- 第一个单词是命令的名字,剩余的单词是命令的参数
- 参数的具体含义仅取决于命令自身的定义
- 单词(Word)可以通过分组(Group)以允许特殊字符(比如空白字符)的出现
- 双引号:
"..."
中的内容允许替换的发生 - 大括号:
{...}
中的内容不允许替换的发生 - 方括号:
[...]
中的内容作为一条命令执行,并整体替换为执行结果
- 双引号:
- 单词(Word)在解析过程中会发生替换
- 变量替换:比如
set var "value = $name"
中的$name
- 命令替换:比如
set var "value = [expr 3+4]
中的[expr 3+4]
- 转义替换:比如
\n
表示换行符,\t
表示制表符,等
- 变量替换:比如
- 字符
#
开始的行是注释
0x01: Tcl脚本由命令序列组成
一个简单的Tcl脚本看起来如下
set name "noyesno"
puts "My name is $name"
set x 3 ; set y 4
puts [expr $x+$y]
可以看出通常情况下,命令写在一整行里,即“命令以换行符结束”。但有时希望一行可以写多个命令,这是可以通过分号;
进行分隔,即所谓“命令以分号结束”。
Tcl解释器,每次只解析并执行一条命令。这意味着,后续的命令是否有错误(无论是语法错误还是执行错误)不影响当前命令的解析和执行。
0x02: Tcl命令由“单词”(Word) 序列组成
set x 0
while {$x < 9} {
incr x
puts $x
}
比如这条while命令,在Tcl里,从根本上来说,它只是一条普通命令,而不像在其他语言里那样,是一种语法规定。只不过while
循环如此常见和常用,Tcl的发行包里提供了这条命令的默认实现。
在这条命令里,总共有3个单词(Word),第一个是命令名,后两个分别是循环的条件判断和循环的执行内容。需要指出的是,这里后两个参数所代表的意义实际上是针对while
这个命令来说的。如果while
命令的含义不像我们所理解的那样是一个循环的话(而这在Tcl里实际上是允许的),那么后两个参数的含义是不明确的。这也就是所谓的“参数的具体含义仅取决于命令自身的定义”。
0x03: Tcl命令单词的分组
在上面的介绍里,我们提到了三种分组,即分号,大括号和方括号。而实际上方括号虽然有分组的作用,实质上则更接近替换的概念。因此,我们说分组时,实际上主要是指分号和大括号这两种。而分号和大括号的区别则主要在于是否允许替换。
上面已经提到的while循环的例子里已经用到了分组,是用的大括号。我们可以看到在大括号中出现了变量,也出现了换行符。变量因为出于大括号中没有被替换,换行符也因为出于大括号中而没有“结束”当前命令。
如果把上面例子中的大括号换成双引号呢?
set x 0
while "$x < 9" "
incr x
puts $x
"
这在语法上是没有错误的。while命令仍然接受了两个参数,只是条件判断部分变成了0 < 9
,执行内容部分变成了了 incr x ; puts 0
。看到这里,就比较容易意识到,我们的while循环发生了死循环,并且每次循环都会输出同样的数字。它等效于:
set x 0
while {0 < 9} {
incr x
puts 0
}
方括号在提供替换功能的同时,实际上也起到了分组的作用。最简单的例子比如
set numbers [list 1 2 3 4 5 6]
这条命令,有三个单词(Word),而不是更多。其中第三个[list 1 2 3 4 5 6]
就是所谓的命令替换形式的分组。
0x04: 单词(Word)在解析过程中会发生替换
Tcl里的替换从概念上说有三种:变量替换,命令替换,转义替换。
可以用一个简单的例子来同时看一下这三种情况。
set name "noyesno"
set text "My name is $name.\nMy age is [expr 3+4]."
这个例子中的$name
即变量替换,\n
即转义替换,[expr 3+4]
即命令替换。
对于替换,除了大括号会阻止替换的发生外,其他的形式——双引号或者没有任何括号——都允许替换的发生。
0x05: 字符#开始的行是注释
在Tcl中,默认的注释方式是字符#
开始的行。这很容易理解。但在Tcl里会有一些“奇怪”的情形。
比如下面的例子里,注释符号#
之前的分号;
是必须的。
set name "noyesno" ;# this is a comment
分号的本身含义是命令的结束,或者分隔两条相邻的命令。从这个角度讲,注释符号#
似乎可以看作是一个特殊的命令。
下面的例子,则通过一个行尾的反斜线云逊注释行跨行。只有第二个puts命令会得到执行。第一个puts行则变成了注释的一部分。
# comment cross lines \
puts 123
puts 456
这个特性在需要将Tcl脚本和Shell脚本结合时,会显得很有用。
程序的注释虽然乍听之下都不复杂,但却是编写可维护的程序重要部分,更多的关于注释的话题,可以参见 Tcl中的注释技巧