爱上TCL,学会TCL
作者:互联网
爱上TCL,学会TCL
文章目录
文章或多或少都会有写瑕疵,不过我会持续的更新~
序言
在仿真的工程中,会有很多的地方用到tcl脚本。不论是编译xilnx_ip还是代码的综合,或者说是别的什么。TCL脚本的使用可以帮助我们提高工作的效率和节省大量的时间。其实很多的知识点我们平时都已经接触到了,但是我还是想通过自己的整理和叙述。让TCL脚本变得不再那么的神秘和未知。
当然,在写这篇文章的时候,我的脑海里是希望大家所有遇到的问题可以在里面找得到,有解释,会应用。但是我考虑到自己的能力问题,想要明白更深层次的细节,这篇文章是远远不够的,希望大家可以帮助我补充修改,让这篇文章能够帮助大家。
第1章 TCL入门篇
1.1 简介
Tcl(读作 tickle)诞生于 80 年代的加州大学伯克利分校,作为一种简单高效可移植性好的脚本语言,目前已经广泛应用在几乎所有的 EDA 工具中。Tcl 的最大特点就是其语法格式极其简单甚至可以说僵化,采用纯粹的 [命令 选项 参数] 形式,是名副其实的“工具命令语言”( 即Tcl 的全称Tool Command Language)。实际上Tcl 的功能可以很强大,用其编写的程序也可以很复杂,但要在Vivado 或大部分其它 EDA 工具中使用,则只需掌握其中最基本的几个部分。
1.2 本文约定
本文为了便于速查和速学,所以每课分成讲解和例子两部分,主要是围绕着例子进行讲解。文字不多。
注:下面的所有章节例子中,'%'为TCL的命令提示符,输入命令回车后,TCL会在接着的一行输出命令执行结果。 |
第2章 基础篇
2.1 脚本、命令和单词符号
注释符号是#或者;#
在命令后注释只能用 ;#
在行开头两者均可;
# ok ;# 正确
;# ok ;# 正确
一个TCL脚本可以包含一个或多个命令。命令之间必须用换行符或分号隔开。
set a 1
set b 2
或
set a 1;set b 2
TCL的每一个命令包含一个或几个单词,第一个单词代表命令名,另外的单词则是这个命令的参数,单词之间必须用空格或TAB键隔开。
2.1.1 puts
关于puts的用法,相信大家都会用到,很简单的例子。比如例2.1:
set orig_proj_dir "[file normalize "$origin_dir/topsims_project"]"
puts "i want to know the path:$orig_proj_dir"
这些puts语句都是我自己添加的,我想知道我的工程的路径是什么~ 然后我们需要source一下XXX.tcl脚本。从log文件我们可以得到信息:
puts的语法很奇妙,当我们对于某一个变量不清楚的时候,puts一下,问题就解决了。这就是它最常用到的方法!
2.2 置换
置换的概念和重要,或许你和我也一样,刚看到这个概念的时候也会一头雾水,但是下面我会用最浅显的语言将这个抽象的概念讲清楚。
TCL解释器对一个命令的求值过程分为两部分:分析和执行。在分析阶段,TCL 解释器运用规则把命令分成一个个独立的单词,同时进行必要的置换(substitution); 在执行阶段,TCL 解释器会把第一个单词当作命令名,并查看这个命令是否有定义,如果有定义就激活这个命令对应的C/C++过程,并把所有的单词作为参数传递给该命令过程,让命令过程进行处理。
或许这些文字你并没有了解,别灰心,下面举个例子,一切问题都迎刃而解。
TCL解释器在分析命令时,把所有的命令参数都当作字符串看待,例如2.2.1:
%set x 10 ;#定义变量x,并把x的值赋为10
=>10
%set y x+100 ;#y的值是x+100,而不是我们期望的110
=>x+100
上例的第二个命令中,x被看作字符串x+100的一部分,如果我们想使用x的值’10’ ,就必须告诉TCL解释器:我们在这里期望的是变量x的值,而非字符’x’。怎么告诉TCL解释器呢,这就要用到TCL语言中提供的置换功能。
TCL提供三种形式的置换:变量置换、命令置换和反斜杠置换。每种置换都会导致一个或多个单词本身被其他的值所代替。置换可以发生在包括命令名在内的每一个单词中,而且置换可以嵌套。
2.2.1 变量置换
变量置换由一个$符号标记,变量置换会导致变量的值插入一个单词中。例如2.2.1.1:
%set y $x+100 ;#y的值是10+100,这里x被置换成它的值10
=>10+100
这时,y的值还不是我们想要的值110,而是10+100,因为TCL解释器把10+100看成是一个字符串而不是表达式,y要想得到值110,还必须用命令置换,使得TCL会把10+100看成一个表达式并求值。
2.2.2命令置换 (command substitution)
命令置换是由[]括起来的TCL命令及其参数,命令置换会导致某一个命令的所有或部分单词被另一个命令的结果所代替。例如2.2.2.1:
%set y [expr $x+100]
=>110
y的值是110,这里当TCL解释器遇到字符’['时,它就会把随后的expr作为一个命令名(注意expr是算数表达式),从而激活与expr对应的C/C++过程,并把’expr’和变量置换后得到的’10+110’传递给该命令过程进行处理。
如果在上例中我们去掉[],那么TCL会报错。因为在正常情况下,TCL解释器只把命令行中的第一个单词作为看作命令,其他的单词都作为普通字符串处理,看作是命令的参数。当然只要牵扯到算数运算都需要[]和expr。
注意,[]中必须是一个合法的TCL脚本,长度不限。
[]中脚本的值为最后一个命令的返回值,例如2.2.2.2:
%set y [expr $x+100;set b 300] ;#y的值为300,因为set b 300的返回值为300
=>300
有了命令置换,实际上就表示命令之间是可以嵌套的,即一个命令的结果可以作为别的命令的参数。
2.2.3 反斜杠置换 (backslash substitution)
TCL语言中的反斜杠置换类似于C语言中反斜杠的用法,主要用于在单词符号中插入诸如换行符、空格、[、$等被TCL解释器当作特殊符号对待的字符。例如:
set msg multiple\ space ;#msg的值为multiple space
如果没有’‘的话,TCL会报错,因为解释器会把这里最后两个单词之间的空格认为是分隔符,于是发现set命令有多于两个参数,从而报错。加入了’'后,空格不被当作分隔符,'multiple space’被认为是一个单词(word)。
细节,你可以想一下,tommi\后面要是多了一个空格,会有什么要的结果? 举个例子,大家常见的错误!例2.2.3.1:
%set name Tommi\
%Wei
答案会报错,因为语法不规范!Tommi\ 后面不应该还有空格
又例如2.2.3.2:
%set msg money\ \$3333\ \nArray\ a\[2]
#这个命令的执行结果为:money $3333
Array a[2]
这里的$不再被当作变量置换符。
(也可以理解为’\’是照妖镜,你有了它,换行符、空格、[、$等字符就会现出原形,失去本领。这句话只是为了方便大家理解。语法是讲究细节的。一个合格的程序猿,不是挨踢员,也不是,攻城狮,错了错了,就是搬砖的吧。也是要有自我修养才行。)
关于这一节的内容,不知不觉中,我已经写了很多的内容,或许你已经有些疲惫,不过有个好消息就是,马上就结束了,在坚持一下。
补充说明:TCL支持以下的反斜杠置换:
序号 | 字符 | 输出 | 十六进制 |
---|---|---|---|
1 | \a | 响铃 | \x07 |
2 | \b | 回车 | \0x08 |
3 | \f | 清屏 | \0x0c |
4 | \n | 换行 | \0x0a |
5 | \r | 回车 | \0x0d |
6 | \t | 制表符 | \0x09 |
7 | \v | 垂直制表符 | \0x0b |
8 | \ddd | 八进制值 | d=0~7 |
9 | \xhh | 十六进制值 | h=0~9,A ~ F,a ~ f |
例如2.2.3.3:
%set a \x48 ;#对应 \xhh
=>H ;#十六进制的48正好是72,对应H
%set a \110 ;#对应 \ddd
=>H ;#八进制的110正好是72,对应H
;# 对应\newline space,一个命令可以用\newline转到下一行继续
%set a [expr \
2+3]
=>5
除了使用反斜杠外,TCL提供另外两种方法来使得解释器把分隔符和置换符等特殊字符当作普通字符,而不作特殊处理,这就要使用双引号和花括号({})。
TCL解释器对双引号中的各种分隔符将不作处理,但是对换行符及$和[]两种置换符会照常处理。例如2.3.3.4:
%set x 100
=>100 ;# 对空格就没有任何处理
%set y "$x ddd"
=>100 ddd ;# 对$进行了处理
而在花括号中,所有特殊字符都将成为普通字符,失去其特殊意义,TCL解释器不会对其作特殊处理。例如2.3.3.5:
%set y {/n$x [expr 10+100]}
=>/n$x [expr 10+100]
注释
TCL中的注释符是’#’,’#‘和直到所在行结尾的所有字符都被TCL看作注释,TCL解释器对注释将不作任何处理。不过,要注意的是,’#'必须出现在TCL解释器期望命令的第一个字符出现的地方,才被当作注释。
2.3 TCL脚本文件和source命令
通过上一章节的内容,大家应该对TCL脚本有了一些最基本的理解。下面给大家讲一个比较简单的概念。source!
可以将 Tcl 命令保存到一个后缀为.tcl 的文件内,然后用 Tcl 的命令 source 来执行:
source filename
source 命令从文件中读取 Tcl 命令并计算。
以实际中的项目为例,
source compile_xilinx_ip.tcl
2.4 给变量赋值
本节的内容比较重要,这将直接影响到后面的工作是否可以继续的展开,其实往往看起来最简答的事物,却隐藏了很深很深的奥秘。
1. set:给变量赋值,(格式为 set var value) 变量定义和赋值命令。不必指定变量值的类型,因为变量值的类型仅一种字符串。为变量赋值时,会为变量开辟一段内存空间来存储变量值。
2. unset 命令与 set 命令作用相反,它取消变量定义,并释放变量所占的内存空间。(这个我在项目中见到的不多,所以也就一笔带过了。主要是也是自己学疏才浅,自己也没有真正的搞明白!)
例子:2.4.1
;# 给变量X赋一个字符串
set X "This is a string"
;# 给变量Y赋一个数字
set Y 1.24
;# 显示X和Y的内容
puts $X ;# This is a string
puts $Y ;# 1.24
;# 打印一个分隔串
puts "..............................."
;# 打印在一行中,推荐使用双引号
set label "The value in Y is: "
puts "$label $Y"
puts $label$Y
“$” 符实现引用替换,用以引用参数值。
Tcl 对替换只进行一遍解释,对嵌套的”$”不于理睬。如下例:
例子2.4.2
%set foo oo
=>oo
%set dollar foo
=>foo
%set x $$dollar
=>$foo ;#只解释一次,将“$dollar”用 dollar 的值(foo)代替,
;#命令等效为 set x {$foo},大括阻止替换。
% set x {$foo}
=>$foo
%set y $x
=>$foo ; #一轮替换
2.4.1 再议变量
TCL支持两种类型的变量:简单变量和数组。一个TCL的简单变量包含两个部分:名字和值。名字和值都可以是任意字符串。例如一个名为 “1323 7&*: hdgg"的变量在TCL中都是合法的。不过为了更好的使用置换(substitution),变量名最好按C\C++语言中标识符的命名规则命名。 TCL解释器在分析一个变量置换时,只把从$符号往后直到第一个不是字母、数字或下划线的字符之间的单词符号作为要被置换的变量的名字。例如2.4.1.1:空格 符号
% set a 2
=>2
%set a.1 4
=>4
% set b $a.1
=>2.1
在最后一个命令行,我们希望把变量a.1的值付给b,但是TCL解释器在分析时只把$符号之后直到第一个不是字母、数字或下划线的字符(这里是’.’)之间的单词符号(这里是’a’)当作要被置换的变量的名字,所以TCL解释器把a置换成2,然后把字符串“2.1”付给变量b。这显然与我们的初衷不同。当然,如果变量名中有不是字母、数字或下划线的字符,又要用置换,可以用花括号把变量名括起来。例如2.4.1.2:
%set b ${a.1}
=>4
TCL中的set命令能生成一个变量、也能读取或改变一个变量的值。例如:
% set a {kdfj kjdf}
=>kdfj kjdf
%set a
=>kdfj kjdf
这个只有一个参数的set命令读取a的当前值kdfj kjdf。
2.4.2 数组
数组是一些元素的集合。TCL的数组和普通计算机语言中的数组有很大的区别。在TCL中,不能单独声明一个数组,数组只能和数组元素一起声明。数组中,数组元素的名字包含两部分:数组名day和数组中元素monday的名字,TCL中数组元素的名字(下标〕可以为任何字符串。例如2.4.2.1:
set day(monday) 1
set day(tuesday) 2
第一个命令生成一个名为day的数组,同时在数组中生成一个名为monday的数组元素,并把值置为1,第二个命令生成一个名为tuesday的数组元素,并把值置为2。
简单变量的置换已经在前文讨论过,这里讲一下数组元素的置换。除了有括号之外,数组元素的置换和简单变量类似。例2.4.2.2:
set a monday
set day(monday) 1
set b $day(monday) ;#b的值为1,即day(monday)的值。
set c $day($a) ;#c的值为1,即day(monday)的值。
2.5 讲讲 [] “” 和{}
其实我们可以发现,本文将了很多的知识点,在文章的已经出现,这所以这么做的原因很简单。我就是希望可以不断的加深对知识点的理解。
[] 方括号
“[]”完成命令替换。用“[]”将一条命令括起来,命令执行完成后,返回结果。
“” 和{}
双引号和花括号将多个单词组织成一个参数,也是一种替换操作。“”和{}内的替换如何进行呢?一般的原则是在" "内的替换正常进行,而在{}内的替换有可能会被阻止。对花括号内的替换操作可以概括为:如果花括号是用做替换操作,则它会阻止内部的嵌套替换,如例2.4.2.2;如果花括号用做界限符,如过程定义时用做界限过程体时,不阻止替换操作, 其他还有 if 条件语句、循环语句、switch 语句和过程声明、数学表达式等。{}的作用比较特殊,需要根据不同的情况区别处理。
例子2.5.1 命令替换[]例子
% set b [set a 5] ;#set a 5 命令输出的结果赋给 b
=>5
%puts $b
=>5
%set c [expr 5 * 10] ;#将乘式结果赋给 c
=>50
例 2.5.2 {}替换的例子
%set s hello
=>hello
%puts stdout "The length of \"$s \"is [string length $s]" ;# “string length” 命令是计算
=>The length of hello is 5
这里指的注意的是,用到了转义字符
因为最外面已经有“”了,所以为了避免歧义,里面的“”用到了转义字符
%puts stdout {The length of $s is [string length $s]}
=>The length of $s is [string length $s] ;#替换操作被阻止
%set x 10
=>10
% set y 20
=>20
% set z [expr {$x + $y}] ;#expr 表示进行数学运算,要用[]括起来
=>30 ;#用{}来组织算术表达式,不阻止$替换操作
% if { $x == 10} { puts “x=$x”}
=> x=10 ;#在条件语句中,{}用来界定条件体和执行体,不阻止内部替换
第3章 进阶篇
TCL脚本学习最难的应该说是前面2章的内容。因为后续的内容,不需要我们一定要记得很清楚,但是一定要会查阅。
3.1 append和incr
这两个命令提供了改变变量的值的简单手段。
append命令把文本加到一个变量的后面,例如3.1.1:
%set txt hello
=>hello
%append txt "! How are you"
=>hello! How are you
关于incr 命令
incr 命令根据指定的步长来增加或减少参数的值。当步长为负时,减少参数值;当步长为正 时,增加参数值。默认步长为+1。 这里出现了??,解释一下,??里面的内容是可以选择性出现的,并不是一定要求,或许后面还可以添加更多的参数。所以不要当成语法上的错误或者说是很高端的样子。说直白点,一切反动派都是纸老虎。
[语法]:incr varName ?step?
例如3.1.2:
%set a 10; incr a
=>a=11
%set a 10; incr a –1
=> a=9
3.2 算数运算
讲解:尽管 Tcl 是基于字串操作的,但它仍旧提供了有效数学运算和逻辑运算的功能。通过命令 expr 可以实现对数学表达式的分析和计算。
1. 操作符
2. 数学函数
例子:3.2.1数学运算举例
TCL支持常用的数学函数,表达式中数学函数的写法类似于C\C++语言的写法,数学函数的参数可以是任意表达式,多个参数之间用逗号隔开。例如3.2.1:
%set x 2
=>2
%expr 2* sin($x<3)
=>1.68294196962
其中expr是TCL的一个命令,语法为: expr arg ?arg …?
再次强调,两个?之间的参数表示可省,后面介绍命令时对于可省参数都使用这种表示形式。 expr可以有一个或多个参数,它把所有的参数组合到一起,作为一个表达式,然后求值。例如3.2.2:
%expr 1+2*3
=>7
需要注意的一点是,数学函数并不是命令,只在表达式中出现才有意义。例如3.2.3:
% set var1 5
% set var2 3
% set res [expr $var1 /$var2]
=> 1 ;#因为 var1 和 var2 的值都是整数,结果也只取整数部分值
% set var1 5.0
% set res [expr $var1 /$var2]
=>1.66666666667 ;#结果为浮点数
%set var1 3
%incr var1 2
=>5
%puts $var1 => 5
%incr var1 –4
=> 1 ;#var1 的值现在为 1
3.2.1 List
list这个概念在TCL中是用来表示集合的。TCL中list是由一堆元素组成的有序集合,list可以嵌套定义,list每个元素可以是任意字符串,也可以是list。下面都是TCL中的合法的list:
{} //空list
{a b c d}
{a {b c} d} //list可以嵌套
list是TCL中比较重要的一种数据结构,对于编写复杂的脚本有很大的帮助,TCL提供了很多基本命令对list进行操作,下面一一介绍:
语法: list ? value value…?
这个命令生成一个list,list的元素就是所有的value。例3.2.1.1:
% list 1 2 {3 4}
=>1 2 {3 4}
3.2.2 concat命令
语法:concat list ?list…?
这个命令把多个list合成一个list,每个list变成新list的一个元素。
举例子说明~合并两个list。例如3.2.2.1
set a {0 1 2}
puts $a
set b {k f c}
puts $b
set ab [concat $a $b] ;# 合并两个 list
puts $ab
0 1 2
k f c
0 1 2 k f c
3.2.3 lindex命令
语法:lindex list ?index1 index2 …indexN?
返回list的第index个(0-based)元素。例3.2.3.1
% lindex {1 2 {3 4}} 2
=>3 4
注意:{3 4}是一个整体
3.2.4 llength命令
语法:llength list
返回list的元素个数。例3.2.4.1
% llength {1 2 {3 4}}
=>3
3.2.5 lreplace命令:
语法:lreplace list first last ?value1? ?value2? …valueN?
使用新元素替换指定索引范围内的旧值,并返回新的列表。first和last分别是被替换的元素的第一个和最后一个元素的索引。与lindex一样支持string index。如果只提供了索引,但是没有提供新的元素,则会删除索引范围内的元素。first与last的不同值会导致不同的行为,例如3.2.5.1:
% lreplace {1 7 8 {9 10} 2 {3 4}} 3 3
=>1 7 8 2 {3 4}
注意:{9 10}被删除了。这就是我说为什么不用时刻记住的原因了,有问题我们可以自己查找文档即可。
% lreplace {1 7 8 2 {3 4}} 4 4 4 5 6
=>1 7 8 2 4 5 6
注意:{3 4}已经换成了4 5 6
3.2.6 lrange 命令:
语法:lrange list first last
返回list的第first (0-based)到第last (0-based)元素组成的串,如果last的值是end。就是从第first个直到串的最后。
例3.2.6.1:
% lrange {1 7 8 2 4 5 6} 3 end
=>2 4 5 6
注意:其实也可以理解为裁剪出我们需要的内容。
3.2.7 lappend命令:
语法:lappend varname value ?value…?
把每个value的值作为一个元素附加到变量varname后面,并返回变量的新值,如果varname不存在,就生成这个变量。例3.2.7.1:
% lappend a 1 2 3
=>1 2 3
% set a
=>1 2 3
3.2.8 lsearch 命令:
语法:lsearch ?-exact? ?-glob? ?-regexp? list pattern
返回list中第一个匹配模式pattern的元素的索引,如果找不到匹配就返回-1。
-exact、-glob、 -regexp是三种模式匹配的技术。-exact表示精确匹配;-glob的匹配方式和string match命令的匹配方式相同,将在后面介绍string命令时介绍;-regexp表示正规表达式匹配,缺省时使用-glob匹配。例:3.2.8.1:
% set a {how are you}
how are you
% lsearch $a y* ;#you
=>2
% lsearch $a y?
=>-1
3.2.9 lsort命令:
语法:lsort ?options? list
这个命令返回把list排序后的串。options可以是如下值:
-ascii 按ASCII字符的顺序排序比较.这是缺省情况。
-dictionary 按字典排序,与-ascii不同的地方是:
(1)不考虑大小写
(2)如果元素中有数字的话,数字被当作整数来排序.
因此:bigBoy排在bigbang和bigboy之间, x10y 排在x9y和x11y之间.
-integer 把list的元素转换成整数,按整数排序.
-real 把list的元素转换成浮点数,按浮点数排序.
-increasing 升序(按ASCII字符比较)
-decreasing 降序(按ASCII字符比较)
-command command TCL自动利用command命令把每两个元素一一比较,然后给出排序结果。
3.2.10 split命令:
语法:split string ?splitChars?
把字符串string按分隔符splitChars分成一个个单词,返回由这些单词组成的串。如果splitChars是一个空字符{},string被按字符分开。如果splitChars没有给出,以空格为分隔符。例3.2.10.1:
% split "how.are.you" . ;# 点对点就抵消了
how are you
% split "how are you" ;# 默认空格
how are you
% split "how are you" {}
h o w { } a r e { } y o u
3.2.11 join命令
语法:join list ?joinString?
join命令是split命令的逆过程。这个命令把list的所有元素合并到一个字符串中,中间以joinString分开。缺省的joinString是空格。例3.2.11.1:
% join {how{}are{}you} {} ;#缺省的状态时空格,{} {}相互抵消
=>how are you
% join {how are you} .
=>how.are.you
3.3 控制流
3.3.1 if命令
讲解:
1. 条件式结果
FALSE TRUE
数值 0 非零
yes / no no yes
true / false false true
if {$x>0} {
.....
}elseif{ $x==1 } {
.....
}elseif { $x==2 } {
....
}else{
.....
}
注意,上例中’{'一定要写在上一行,因为如果不这样,TCL 解释器会认为if命令在换行符处已结束,下一行会被当成新的命令,从而导致错误的结果。在下面的循环命令的书写中也要注意这个问题。书写中还要注意的一个问题是if 和{之间应该有一个空格,否则TCL解释器会把’if{'作为一个整体当作一个命令名,从而导致错误。
乌龟的屁股——龟腚!
例子:3.3.1
set x 1;
if {$x == 2} {puts "$x is 2"} else {puts "$x is not 2"} ;#判断是否相等使用 ==
if {$x != 1} { ;#判断是否不等使用 !=
puts "$x is != 1"
} else {
puts "$x is 1"
}
记住一种格式就可以了。通常项目中也可以这么写。
if {[file extension $file]==".xcix"} {
generate_target all $file_obj
} else {
generate_target Simulation $file_obj
}
3.3.2 WHILE 循环
讲解:
1.while后面的条件表达式是放在花括号中的
语法为: while test body
参数test是一个表达式,body是一个脚本,如果表达式的值非0,就运行脚本,直到表达式为0才停止循环,此时while命令中断并返回一个空字符串。
例子:3.3.2.1
set x 1;
while {$x < 5} {puts "x is $x"; set x [expr $x + 1]}
puts "exited first loop with X equal to $x\n"
set x 0;
while "$x < 5" { ;#只执行一次置换1<5,于是该条件永远为真
set x [expr $x + 1]
if {$x >6} break; ;#如果去掉这句就成了死循环
if "$x > 3" continue; ;#这句使4 打不出来
puts "x is $x";
}
puts "exited second loop with X equal to $x\n"
感觉这里对break 和continue 的描述不清晰。在循环体中,可以用break和continue命令中断循环。其中break命令结束整个循环过程,并从循环中跳出,continue只是结束本次循环。
while {[gets $xci_source file] > -1} {
set file $file
set file [file normalize $origin_dir/../../../source/xilinx_ip/$file]
add_files -norecurse $file
set file_obj [get_files $file -of_objects [get_filesets sources_1]]
if {[file extension $file]==".xcix"} {
generate_target all $file_obj
} else {
generate_target Simulation $file_obj
}
}
摘录项目中的用法。
例如3.3.2.2:
假设变量 a 是一个链表,下面的脚本把a 的值复制到b:
set b " " ;#空的文本
set i [expr [llength $a] -1] ;#假如a=apple ,i=4
while {$i>=0}{
lappend b [lindex $a $i] ;# a = apple 4 e l p p a
incr i -1
}
3.3.3 FOR循环和incr
讲解:
- incr x 和 set x [expr $x + 1] 达到一样的效果,向上加一。这里不再赘述
- for命令
语法为: for init test reinit body
参数init是一个初始化脚本,第二个参数test是一个表达式,用来决定循环什么时候中断,第三个参数reinit是一个重新初始化的脚本,第四个参数body也是脚本,代表循环体。下例与上例作用相同:
set b " "
for {set i [expr [llength $a] -1]} {$i>=0} {incr i -1} {
lappend b [lindex $a $i]}
for {set i 1} {$i < 10} {incr i} body
3.3.4 foreach命令
这个命令有两种语法形式
- foreach varName list body
第一个参数varName是一个变量,第二个参数list 是一个表(有序集合),第三个参数body是循环体。每次取得链表的一个元素,都会执行循环体一次。 下例与上例作用相同:
set b " "
foreach i $a{
set b [linsert $b 0 $i]
}
- foreach varlist1 list1 ?varlist2 list2 …? Body
这种形式包含了第一种形式。第一个参数varlist1是一个循环变量列表,第二个参数是一个列表list1,varlist1中的变量会分别取list1中的值。body参数是循环体。 ?varlist2 list2 …?表示可以有多个变量列表和列表对出现。例如:
set x {}
foreach {i j} {a b c d e f} {
lappend x $j $i
}
这时总共有三次循环,x的值为"b a d c f e"。
set x {}
foreach i {a b c} j {d e f g} {
lappend x $i $j
}
这时总共有四次循环,x的值为"a d b e c f {} g"。
set x {}
foreach i {a b c} {j k} {d e f g} {
lappend x $i $j $k
}
这时总共有三次循环,x的值为"a d e b f g c {} {}"。
用法很独特,很独特。
3.3.5 switch 命令
和C语言中switch语句一样,TCL中的switch命令也可以由if命令实现。只是书写起来较为烦琐。 switch命令的语法为: switch ? options? string { pattern body ? pattern body …?}
第一个是可选参数options,表示进行匹配的方式。TCL支持三种匹配方式:-exact方式,-glob方式,-regexp方式, 缺省情况表示-glob方式。-exact方式表示的是精确匹配,-glob方式的匹配方式和string match 命令的匹配方式相同(第八节介绍),-regexp方式是正规表达式的匹配方式。第二个参数string 是要被用来作测试的值,第三个参数是括起来的一个或多个元素对,例:
switch $x {
a -
b {incr t1}
c {incr t2}
default {incr t3}
}
其中a的后面跟一个’-'表示使用和下一个模式相同的脚本。default表示匹配任意值。一旦switch命令 找到一个模式匹配,就执行相应的脚本,并返回脚本的值,作为switch命令的返回值。
第4章 提升篇
4.1 第1课:过程PROC
讲解:
格式:proc name args body
TCL中过程是由proc命令产生的:
例如:
% proc add {x y} {expr
x
+
x+
x+y}
proc命令的第一个参数是你要定义的过程的名字,第二个参数是过程的参数列表,参数之间用空格隔开,第三个参数是一个TCL脚本,代表过程体。 proc生成一个新的命令,可以象固有命令一样调用:
% add 1 2
=>3
在定义过程时,你可以利用return命令在任何地方返回你想要的值。 return命令迅速中断过程,并把它的参数作为过程的结果。例如:
% proc abs {x} {
if {$x >= 0} { return KaTeX parse error: Expected 'EOF', got '}' at position 3: x }̲ return [expr -x]
}
abs(arg)取绝对值,-$x取反。过程的返回值是过程体中最后执行的那条命令的返回值。
4.1.1变量的作用域
对于在过程中定义的变量,因为它们只能在过程中被访问,并且当过程退出时会被自动删除,所以称为局部变量;在所有过程之外定义的变量我们称之为全局变量。TCL中,局部变量和全局变量可以同名,两者的作用域的交集为空:局部变量的作用域是它所在的过程的内部;全局变量的作用域则不包括所有过程的内部。这一点和C语言有很大的不同.
如果我们想在过程内部引用一个全局变量的值,可以使用global命令。例如:
%set a 4
=>4
%proc sample {x} {
global a
incr a
return [expr $a+$x]
}
% sample 3
=>8
%set a
=>5
全局变量a在过程中被访问。在过程中对a的改变会直接反映到全局上。如果去掉语句global a,TCL会出错,因为它不认识变量a.
4.1.2 缺省参数和可变个数参数
TCL还提供三种特殊的参数形式:
首先,你可以定义一个没有参数的过程,例如:
proc add {} {expr 2+3}
其次,可以定义具有缺省参数值的过程,我们可以为过程的部分或全部参数提供缺省值,如果调用过程时未提供那些参数的值,那么过程会自动使用缺省值赋给相应的参数。和C\C++中具有缺省参数值的函数一样,有缺省值的参数只能位于参数列表的后部,即在第一个具有缺省值的参数后面的所有参数,都只能是具有缺省值的参数。
例如:
proc add {val1 {val2 2} {val3 3}}{
expr
v
a
l
1
+
val1+
val1+val2+$val3
}
则:
add 1 ;#值为6,此时val1=1
add 2 20 ;#值为25, 此时 val1=2, val2 =20
add 4 5 6 //值为15, 此时 val1= 4, val2= 5, val6= 6
另外,TCL的过程定义还支持可变个数的参数,如果过程的最后一个参数是args, 那么就表示这个过程支持可变个数的参数调用。调用时,位于args以前的参数象普通参数一样处理,但任何附加的参数都需要在过程体中作特殊处理,过程的局部变量args将会被设置为一个列表,其元素就是所有附加的变量。如果没有附加的变量,args就设置成一个空串,下面是一个例子:
proc add {val1 args} {
set sum $val1
foreach i $args {
incr sum $i
}
return $sum
}
则:
add 2 ;#值为2
add 2 3 4 5 6 ;#值为20
*4.1.3 引用:upvar
命令语法:upvar ?level? otherVar myVar ?otherVar myVar …?
upvar命令使得用户可以在过程中对全局变量或其他过程中的局部变量进行访问。 upvar命令的第一个参数otherVar是我们希望以引用方式访问的参数的名字,第二个参数myVar 是这个过程中的局部变量的名字,一旦使用了upvar 命令把otherVar 和myVar 绑定,那么在过程中对局部变量myVar 的读写就相当于对这个过程的调用者中otherVar 所代表的局部变量的读写。下面是一个例子:
% proc temp {arg} {
upvar $arg b
set b [expr $b+2]
}
% proc myexp {var} {
set a 4
temp a
return [expr
v
a
r
+
var+
var+a]
}
则:
% myexp 7 ;# var=7 a=4 b= 4+2
=>13
这个例子中,upvar 把$arg(实际上是过程myexp中的变量a)和过程temp中的变量b绑定,对b的读写就相当于对a的读写。
upvar命令语法中的level参数表示:调用upvar命令的过程相对于我们希望引用的变量myVar在调用栈中相对位置。例如:
upvar 2 other x
这个命令使得当前过程的调用者的调用者中的变量other,可以在当前过程中利用x访问。缺省情况下,level的值为1,即当前过程(上例中的temp)的调用者(上例中的myexp)中的变量(上例中myexp的a)可以在当前过程中利用局部变量(上例中temp的b)访问。
如果要访问全局变量可以这样写:
upvar #0 other x
那么,不管当前过程处于调用栈中的什么位置,都可以在当前过程中利用x访问全局变量other。
4.2 字符串函数
因为TCL把所有的输入都当作字符串看待,所以TCL提供了较强的字符串操作功能,TCL中与字符串操作有关的命令有:string、format、regexp、regsub、scan等。
4.2.1 format命令
语法:format formatstring ?vlue value…?
format命令类似于ANSIC中的sprintf函数和MFC中CString类提供的Format成员函数。它按formatstring提供的格式,把各个value的值组合到formatstring中形成一个新字符串,并返回。例如:
%set name tommi
=>tommi
%set age 29
=>29
%set msg [format “%s is %d years old” $name $age]
tommi is 29 years old
4.2.2 scan命令
语法:scan string format varName ?varName …?
scan命令可以认为是format命令的逆,其功能类似于ANSI C中的sscanf函数。它按format提供的格式分析string字符串,然后把结果存到变量varName中, 注意除了空格和TAB键之外,string和format中的字符和’%'必须匹配。例如:
% scan "some 26 34" "some %d %d" a b
=>2
% set a
=>26
% set b
=>34
% scan "12.34.56.78" "%d.%d.%d.%d" c d e f
=>4
% puts [format "the value of c is %d,d is %d,e is %d ,f is %d" $c $d $e $f]
=>the value of c is 12,d is 34,e is 56 ,f is 78
scan命令的返回值是匹配的变量个数。而且,我们发现,如果变量varName不存在的话,TCL会自动声明该变量。
4.3 string命令
讲解:
1.字符串函数列表
序列 函数 解释
1 string length 返回字符串的长度
2 string index 返回字符串相应位置的字符
3 string range 返回字符串中一个范围内的字符子串
例子:018_string.tcl
set string “this is my test string”
puts “There are [string length KaTeX parse error: Can't use function '\"' in math mode at position 23: … characters in \̲"̲string”"
puts “[string index KaTeX parse error: Can't use function '\"' in math mode at position 38: …d character in \̲"̲string”" ;#返回h
puts “”[string range $string 5 10]" are characters between the 5’th and 10’th" ;#返回”is my ”
4.3.1 更多字符串函数
讲解:
1.字符串函数列表
序号 函数 解释
1 string compare string1 string2 字符串比较
返回:
-1 :string1比string2小
0 :string1和string2相等
1 :string1比string2大
2 string first string1 string2 返回string1在string2中第一次出现的位置;如果string2不在string1中,返回-1
3 string last string1 string2 返回string1在string2中最后一次出现的位置;如果string2不在string1中,返回-1
4 string wordstart string1 index 返回string1中index处的单词的开始位置
5 string wordend string1 index 返回string1中index处的单词的结束位置
6 string match pattern string1 返回string1中是否满足匹配模式pattern
匹配模式的通配符:
- :任意字符
? :单个字符
\X :转义符
[…] :字符区间,例如:[a-z]
4.4 文件
TCL提供了丰富的文件操作的命令。通过这些命令你可以对文件名进行操作(查找匹配某一模式的文件)、以顺序或随机方式读写文件、检索系统保留的文件信息(如最后访问时间)。
4.4.1 文件名
TCL中文件名和我们熟悉的windows表示文件的方法有一些区别:在表示文件的目录结构时它使用’/’,而不是’’,这和TCL最初是在UNIX下实现有关。比如:
set origin_dir “…/comp_xilinx”
*4.4.2 基本文件输入输出命令
定义名为tgrep的过程,可以说明TCL文件I/O的基本特点:
proc tgrep {pattern filename} {
set f [open $filename r]
while {[gets $f line]} {
if {[regexp $pattern $line]} {
puts stdout $line
}
}
close $f
}
以上过程非常象UNIX的grep命令, 你可以用两个参数调用它,一个是模式,另一个是文件名,tgrep将打印出文件中所有匹配该模式的行。
下面介绍上述过程中用到的几个基本的文件输入输出命令。
open name ?access?
open命令 以access方式打开文件name。返回供其他命令(gets,close等)使用的文件标识。如果name的第一个字符是“|”,管道命令被触发,而不是打开文件。
文件的打开方式和我们熟悉的C语言类似,有以下方式:
r 只读方式打开。文件必须已经存在。这是默认方式。
r+ 读写方式打开,文件必须已经存在。
w 只写方式打开文件,如果文件存在则清空文件内容,否则创建一新的空文件。
w+ 读写方式打开文件,如文件存在则清空文件内容,否则创建新的空文件。
a 只写方式打开文件,文件必须存在,并把指针指向文件尾。
a+ 读写方式打开文件,并把指针指向文件尾。如文件不存在,创建新的空文件。
open命令返回一个字符串用于表识打开的文件。当调用别的命令(如:gets,puts,close,〕对打开的文件进行操作时,就可以使用这个文件标识符。TCL有三个特定的文件标识: stdin,stdout和stderr,分别对应标准输入、标准输出和错误通道,任何时候你都可以使用这三个文件标识。
gets fileId ?varName? 读fileId标识的文件的下一行,忽略换行符。如果命令中有varName就把该行赋给它,并返回该行的字符数(文件尾返回-1),如果没有varName参数,返回文件的下一行作为命令结果(如果到了文件尾,就返回空字符串)。
和gets类似的命令是read,不过read不是以行为单位的,它有两种形式:
read ?-nonewline? fileId 读并返回fileId标识的文件中所有剩下的字节。如果没有nonewline开关,则在换行符处停止。
read fileId numBytes 在fileId标识的文件中读并返回下一个numbytes字节。
puts ?-nonewline? ?fileId? string puts命令把string写到fileId中,如果没有nonewline开关的话,添加换行符。fileId默认是stdout。命令返回值为一空字符串。
puts命令使用C的标准I/O库的缓冲区方案,这就意味着使用puts产生的信息不会立即出现在目标文件中。如果你想使数据立即出现在文件中,那你就调用flush命令:
flush fileId 把缓冲区内容写到fileId标识的文件中,命令返回值为空字符串。
flush命令迫使缓冲区数据写到文件中。flush直到数据被写完才返回。当文件关闭时缓冲区数据会自动flush。
close ?fileId? 关闭标识为fileId的文件,命令返回值为一空字符串。
这里特别说明的一点是,TCL中对串口、管道、socket等的操作和对文件的操作类似,以上对文件的操作命令同样适用于它们。
*4.4.3 随机文件访问
默认文件输入输出方式是连续的:即每个gets或ead命令返回的是上次gets或ead访问位置后面的字节,每个puts命令写数据是接着上次puts写的位置接着写。TCL提供了seek,tell和eof等命令使用户可以非连续访问文件。
每个打开的打开文件都有访问点,即下次读写开始的位置。文件打开时,访问点总是被设置为文件的开头或结尾,这取决于打开文件时使用的访问模式。每次读写后访问位置按访问的字节数后移相应的位数。
可以使用seek命令来改变文件的访问点:
seek fileId offset ?origin? 把fileId标识的文件的访问点设置为相对于origin偏移量为offset的位置。origin可以是start,current,end,默认是start。命令的返回值是一空字符串。
例如:seek fileId 2000 改变fieleId标识的文件访问点,以便下次读写开始于文件的第2000个字节。
seek的第三个参数说明偏移量从哪开始计算。第三个参数必为start,current或end中的一个。start是默认值:即偏移量是相对文件开始处计算。current是偏移量从当前访问位置计算。end是偏移量从文件尾开始计算。
tell fileId 返回fileId标识的文件的当前访问位置。
eof fileId 如果到达fileId标识的文件的末尾返回1,否则返回0。
4.4.4 当前工作目录
TCL提供两个命令来管理当前工作目录:pwd和cd。
pwd和UNIX下的pwd命令完全一样, 没有参数,返回当前目录的完整路径。
cd 命令也和UNIX命令也一样,使用一个参数,可以把工作目录改变为参数提供的目录。如果cd 没使用参数,UNIX下,会把工作目录变为启动TCL脚本的用户的工作目录,WINDOWS下会把工作目录变为windows操作系统的安装目录所在的盘的根目录(如:C:/)。值得注意的是,提供给cd的参数中路径中的应该用’/‘而不是’’。如 cd C:/TCL/lib。这是UNIX的风格。
4.4.5 文件操作和获取文件信息
TCL提供了两个命令进行文件名操作:glob和file,用来操作文件或获取文件信息。
glob命令采用一种或多种模式作为参数,并返回匹配这个(些)模式的所有文件的列表,其语法为:
glob ?switches? pattern ?pattern …?
其中switches可以取下面的值:
-nocomplain :允许返回一个空串,没有-nocomplain时,如果结果是空的,就返回错误。
– :表示switches结束,即后面以’-'开头的参数将不作为switches。
glob命令的模式采用string match命令(见8.5.7节)的匹配规则。例如:
%glob .c .h
main.c hash.c hash.h
返回当前目录中所有.c或.h的文件名。 glob 还允许模式中包含’ 括在花括号中间以逗号分开的多种选择’,例如 :
%glob {{src,backup}/.[ch]}
src/main.c src/hash.c src/hash.h backup/hash.c
下面的命令和上面的命令等价:
glob {src/.[ch]} {backup/*.[ch]}
注意:这些例子中模式周围的花括号是必须的,可以防止命令置换。在调用glob命令对应的C过程前这些括号会被TCL解释器去掉。
如果glob的模式以一斜线结束,那将只匹配目录名。例如:
glob */
只返回当前目录的所有子目录。
如果glob返回的文件名列表为空,通常会产生一个错误。但是glob的在样式参数之前的第一个参数是"-nocomplain"的话,这时即使结果为空,glob也不会产生错误。
对文件名操作的第二个命令是file。file是有许多选项的常用命令,可以用来进行文件操作也可以检索文件信息。这节讨论与名字相关的选项,下一节描述其他选项。使用file命令时,我们会发现其中有很明显的UNIX痕迹。
file atime name 返回一个十进制的字符串,表示文件name最后被访问的时间。时间是以秒为单位从1970年1月1日12:00AM开始计算。如果文件name 不存在或查询不到访问时间就返回错误。例:
% file atime license.txt
975945600
file copy ?-force? ?–? source target
file copy ?-force? ?–? source ?source …? targetDir
这个命令把source中指明的文件或目录递归的拷贝到目的地址targetDir,只有当存在-force选项时,已经存在的文件才会被覆盖。试图覆盖一个非空的目录或以一个文件覆盖一个目录或以一个目录覆盖一个文件都会导致错误。–的含义和前面所说的一样。
file delete ?-force? ?–? pathname ?pathname … ? 这个命令删除pathname指定的文件或目录,当指定了-force时,非空的目录也会被删除。即使没有指定-force,只读文件也会被删除。删除一个不存在的文件不会引发错误。
file dirname name 返回name中最后一个“/”前的所有字符;如果 name 不包含“/”,返回“.”;如果name 中最后一个“/”是第name的第一个字符,返回“/”。
file executable name 如果name对当前用户是可以执行的,就返回1,否则返回0。
file exists name 如果name存在于当前用户拥有搜索权限的目录下返回1,否则返回0。
file extension name 返回name中最后的“.”以后(包括这个小数点)的所有字符。如果name中没有“.”或最后斜线后没有“.”返回空字符。
file isdirectory name 如果name是目录返回1,否则返回0。
file isfile name 如果name是文件返回1,否则返回0。
file lstat name arrayName 除了利用lstat内核调用代理stat内核调用之外,和file stat命令一样,这意味着如果name是一个符号连接,那么这个命令返回的是这个符号连接的信息而不是这个符号连接指向的文件的信息。对于不支持符号连接的操作系统,这个命令和和file stat命令一样。
file mkdir dir ?dir …? 这个命令和UNIX的mkdir命令类似,创建dir中指明的目录。如果dir已经存在,这个命令不作任何事情,也不返回错误。不过如果试图用一个目录覆盖已经存在的一个文件会导致错误。这个命令顺序处理各个参数,如果发生错误的话,马上退出。
file mtime name 返回十进制的字符串,表示文件name最后被修改的时间。时间是以秒为单位从1970年1月1日12:00AM开始计算。
file owned name 如果name被当前用户拥有,返回1,否则返回0。
file readable name 如果当前用户可对name进行读操作,返回1,否则返回0。
file readlink name 返回name代表的符号连接所指向的文件。如果name 不是符号连接或者找不到符号连接,返回错误。在不支持符号连接的操作系统(如windows)中选项readlink没有定义。
file rename ? -force? ?–? source target
file rename ?-force? ?–? source ?source …? targetDir
这个命令同时具有重命名和移动文件(夹)的功能。把source指定的文件或目录改名或移动到targetDir下。 只有当存在-force选项时,已经存在的文件才会被覆盖。 试图覆盖一个非空的目录或以一个文件覆盖一个目录或以一个目录覆盖一个文件都会导致错误。
file rootname name 返回name中最后“.”以前(不包括这个小数点)的所有字符。如果name中没有“.”返回Name。
file size name 返回十进制字符串,以字节表示name的大小。如果文件不存在或得不到name的大小,返回错误。
file stat name arrayName 调用stat内核来访问name,并设置arrayName参数来保存stat的返回信息。 arrayName被当作一个数组,它将有以下元素:atime、ctime、dev、gid、ino、mode、mtime、nlink、size、type和uid。除了type以外,其他元素都是十进制的字符串,type元素和file type命令的返回值一样。其它各个元素的含义如下:
atime 最后访问时间.
ctime 状态最后改变时间.
dev 包含文件的设备标识.
gid 文件组标识.
ino 设备中文件的序列号.
mode 文件的mode比特位.
mtime 最后修改时间.
nlink 到文件的连接的数目.
size 按字节表示的文件尺寸.
uid 文件所有者的标识.
这里的atime、mtime、size元素与前面讨论的file的选项有相同的值。要了解其他元素更多的信息,就查阅stat系统调用的文件;每个元都直接从相应stat返回的结构域中得到。 文件操作的stat选项提供了简单的方法使一次能获得一个文件的多条信息。这要比分多次调用file来获得相同的信息量要显著的快。
file tail name 返回name中最后一个斜线后的所有字符,如果没有斜线返回name。
file type name 返回文件类型的字符串,返回值可能是下列中的一个: file、directory、characterspecial、blockSpecial、fifo、link或socket。
file writable name
如果当前用户对name可进行写操作,返回1,否则返回0。
4.5错误和异常
错误和异常处理机制是创建大而健壮的应用程序的必备条件之一,很多计算机语言都提供了错误和异常处理机制,TCL也不例外。
错误(Errors)可以看作是异常(Exceptions)的特例。TCL中,异常是导致脚本被终止的事件,除了错误还包括break、continue 和return等命令。TCL允许程序俘获异常,这样仅有程序的一部分工作被撤销。程序脚本俘获异常事件以后,可以忽略它,或者从异常中恢复。如果脚本无法恢复此异常,可以把它重新发布出去。下面是与异常有关的TCL命令:
catch command ?varName?
catch script ?resultVarName? ?optionsVarName?
这个命令把command作为TCL脚本求值,返回一个整型值表明command结束的状态。如果提供varName参数,TCL将生成变量varName,用于保存command产生的错误消息。
if { ! [catch {glob $PRJ_DIR/*generated.txt} filelist ] } {
eval exec rm $filelist
}
error message ?info? ?code? 这个命令产生一个错误,并把message作为错误信息。如果提供info参数,则被用于初始化全局变量errorInfo。如果提供code参数,将被存储到全局变量errorCode中。
return -code code ?-errorinfo info? ?-errorcode errorCode? ?string? 这个命令使特定过程返回一个异常。code指明异常的类型,必须是ok,error,return,break,continue或者是一个整数。-errorinfo选项用于指定全局变量errorInfo 的初始值,-errorcode用于指定全局变量errorCode的初始值。 string给出return的返回值或者是相关的错误信息,其默认值为空。
4.5.1 使用catch捕获错误
错误通常导致所有活动的TCL命令被终止,但是有些情况下,在错误发生后继续执行脚本是有用的。例如,你用unset取消变量x的定义,但执行unset时,x可能不存在。如果你用unset取消不存在的变量,会产生一个错误:
% unset x
=>can’t unset “x”: no such variable
此时,你可以用catch命令忽略这个错误:
% catch {unset x}
=>1
catch的参数是TCL脚本。如果脚本正常完成,catch返回0。如果脚本中发生错误,catch会俘获错误(这样保证catch本身不被终止掉)然后返回1表示发生了错误。上面的例子忽略unset的任何错误,这样如果x存在则被取消,即使x以前不存在也对脚本没有任何影响。
catch命令可以有第二个参数。如果提供这个参数,它应该是一个变量名,catch把脚本的返回值或者是出错信息存入这个变量。
%catch {unset x} msg
=>1
%set msg
=>can’t unset “x”: no such variable
在这种情况下,unset命令产生错误,所以msg被设置成包含了出错信息。如果变量x存在,那么unset会成功返回,这样catch的返回值为0,msg存放unset命令的返回值,这里是个空串。如果在命令正常返回时,你想访问脚本的返回值,这种形式很有用;如果你想在出错时利用错误信息做些什么,如产生log文件,这种形式也很有用。
4.5.2 TCL eval exec 命令理解
本节内容参考链接:https://blog.csdn.net/yudingding6197/article/details/44453933
exec 就是执行一条命令,更直白的理解就是:如果在Linux的Shell中我们可以运行ls这条命令,但是在tcl环境中,运行ls是不成功,所以通过调用exec ls,就可以运行这条命令了
exec就是运行在shell或者 Windows的cmd窗口中可执行程序
eval优势是运行动态的命令:
在shell中启动 tclsh
% puts abc
% eval puts abc
两条执行的结果是一样的,体会不到eval的好处,因为我们写死了这条命令"puts abc"
如果想执行多条命令,命令是变化的,在 *.tcl 脚本中可以定义字符串变量,变量对应不同的命令,每次最后调用:
eval $cmd
就可以运行不同的命令,动态命令就是变化的命令
4.5.3 TCL中的字符串匹配命令string match
https://www.leanwind.com/archives/6793.html
在TCL中可以使用命令string match进行字符串的匹配,命令格式如下:
string match ?-nocase? pattern string
如果pattern与string匹配则返回1,否则返回0.
如果指定了-nocase选项则不区分大小写,否则在匹配时区分大小写。
在进行字符串匹配时可以使用如下通配符:
- 可以与零个或多个任意字符组成的字符串匹配;
? 可以与一个任意字符匹配;
[chars] 与chars中的任意一个字符匹配,如果chars的内容包括范围表达式a-b,那么a到b之间的任意个字符都可以匹配,包括a和b;
\x 与单个字符x匹配,可以用于指定会被特殊处理的字符,如*?[].
示例:
string match a* alpha
→1
string match a* bat
→0
string match {[ab]} brown
→1
string match a Arizona
→0
string match -nocase a* Arizona
→1
string match {?} "Wow! "
→0
string match {?} "What? "
→1
通配符样式的模式可以完成很多简单的任务。例如,*.[ch]就可以用来匹配所有以.c或.h结尾的字符串。然而,更复杂的匹配模式无法由通配符样式完成。例如,无法由通配符样式的模式检测一个字符串是否包含了所有的数字:模式[1-9]只能与单个数字匹配,却不能指明多个数字用于匹配。更复杂的匹配模式需要使用正则表达式。
参考文献
本文主要参考了《TclTutor 2.0 beta4》
标签:文件,set,string,学会,命令,TCL,file,爱上 来源: https://blog.csdn.net/weixin_46259642/article/details/112192185