草庐IT

Tcl脚本入门笔记详解(一)

一点一点的进步 2025-07-18 原文

TCL脚本语言简介

TCLTool Command Language) 是一种解释执行脚本语言Scripting Language) ,它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。
• 实际上包含了两个部分一个语言一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是一个库包,可以被嵌入应用程序,Tcl的库包含了一个分析器、用于执行内建命令的例程和可以使你扩充(定义新的过程)的库函数。每个应用程序都可以根据自己的需要对TCL语言进行扩展
• 扩展后的TCL语言将可以继承TCL核心部分的所有功能,包括核心命令、控制结构、数据类型、对过程的支持等;TCL良好的可扩展性使得它能很好地适应产品测试的需要,目前已成为自动测
试中事实上的标准。

Tcl基础入门

脚本学习核心

• TCL脚本执行依赖于解释器逐行执行)。
TCL有效命令行命令+字符串结合空格间隔符)形成
• 明白置换($、[]、\)和引用(''",{})的差别和联系。
• 理解命令eval、expr、source、exec的差别。
• 掌握{*}配合glob等返回list后的操作

基础语法

• 一个TCL脚本可以包含一个或多个命令。命令之间必须用换行符或分号隔开,下面的两个脚本都是合法的:

set a 1
set b 2
或set a 1; set b 2

TCL每一命令包含一个或几个单词第一个单词代表命令名,另外的单词则是这个命令的参数,单词之间必须用空格或TAB键隔开

注释:使用#注释set foo 0;  #注释内容,注释符号前应该有分号,因为tcl解析器总是认为一条命令应该以换行或者分号结束,其他的在同一行中都认为是参数。

置换(substitution )

• TCL解释器在分析命令时,把所有的命令参数都当作字符串看待,数据类型:tcl不支持其他语言中的形如int, double ,char等等类型,唯一支持的就是string类型。例如:

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提供三种形式的置换:变量置换、命令置换和反斜杠置换.

变量置换

变量置换由一个$符号标记,变量置换会导致变量的值插入一个单词中。例如:

set y $x+100; #y的值是10+100,这里x被置换成它的值10
10 + 100


•这时,y的值还不是我们想要的值110,而是10+100,因为TCL解释器把10+100看成是一个字符串不是表达式,y要想得到值110,还必须用命令置换,使得TCL会把10+100看成一个表达式并求值

命令置换

命令置换是由[ ]括起来的TCL命令及其参数,命令置换会导致某一个命令的所有或部分单词被另一个命令的结果所代替。例如:

set y [expr $x+100];
110


• y的值是110 , 这里当TCL解释器遇到字符'['时,它就会把随后的expr作为一个命令名,从而激活与expr对应的C/C++过 程,并把'expr'和变量置换后得到的'10+110' 传递给该命令过程进行处理
• 如果在上例中我们去掉 [ ],那么TCL会报错 。因为在正常情况下 ,TCL解释器只把命令行中的第一个单词作为看作命令,其他的单词都作为普通字符串处理,看作是命令的参数

反斜杠置换

• TCL语言中的反斜杠\置换似于C语言中反斜杠的用法,主要用于在单词符号中插入诸如换行符、空格[、$等被TCL解释器当作特殊符号对待的字符。例如:

set msg multiplex\ space;         #msg的值为multiple space。
multiple space


•如果没有'\'的话,TCL会报错,因为解释器会把这里最后两个单词之间的空格认为是分隔符,于是发现set命令有多于两个参数,从而报错。加入了'\‘后,空格不被当作分隔符,'multiple space‘被认为是一个单词(word)。


• TCL支持以下的反斜杠置奂:

 例如:
 

set a \x48 ;    #对应 \xhh
H                   #十六进制的48是72,对应字符H

双引号和花括号

• 除了使用反斜杠外,TCL提供另外两种方法来使得解释器把分隔符和置换符等特殊字符当作普通字符,而不作特殊处理,这就要使用双引号" "和花括号({})
• TCL解释器对双引号中的各种分隔符将不作处理,但是对换行符及$和[ ]两种置换符会照常处理。

set y "$x ddd";
100 ddd


TCL中的注释符'#','#'和直到所在行结尾的所有字符都被TCL看作注释,TC解释器对注释将不作任何处理。

变量

变量:简单变量

 一个TCL简单变量包含两个部分 :名字和值名字和值都可以是任意字符串
TCL解释器在分析一个变量置换时,只把从$符号往后直到第一个不是字母、数字或下划践的字符之间的单词符号作为要被置换的变量的名字。变量:在tcl变量不需要声明就可以直接赋值,

set a 2 ;   set a.1 4;   set b $a.1;
2                  4                  2.1


• 在最后—命令行,我们希望把变量a.1的值付给b,但是TCL解释器在分析时只把$符号之后直到第一个不是字母、数字或下划线的字符(这里是之间的单词符号(这里是'a')当作要被置换的变量的名字,所以TCL解释器把a置换成2,然后把字符串付给变量b。这显然与我们的初衷不同。

•如果变量名中有不是字母、数字或下划线的字符,又要用置换,可以花括号把变量名括起来

set b ${a.1};
4


•TCL中的set命令生成一个变量、也能读取或改变一个变量的值
 

set a {kdfj kjdf} ;   # 变量a的值是kdfj kjdf   
set a;                    #读取变量a的值

变量:数组

数组是一些元素的集合。TCL数组和普通计算机语言中的数组有很大的区别。在TCL中,不能单独声明一个数组,数组只能和数组元素一起声明。数组中,数组元素的名字包含两部分:数组名和数组中元素的名字,TCL中数组元素的名字(下标)可以为任何字符串。

set day(monday) 1;
set day(tuesday) 2;


• 第一个命令生成一个名为 day 的数组 , 同时在数组中生成 一个名为monday的数组元素,并把值置为1,第二个命令生成一个名为tuesday的数组元素,并把值置为2。

相关命令

unset这个命令从解释器中删除变量,它后面可以有任意多个参数,每个参数是一个变量名,可以是简单变量,也可以数组或数组元素。
 

unset a b day(monday)

append命令把文本加到一个变量的后面
 

set txt hello;    #hello
append txt"! How are you";   #hello! How are you

incr命令把一个变量值加上一个整数。incr要求变量原来的值和新加的值都必须是整数
 

set b 2; incr b 3;   #结果为5

表达式

• TCL支持常用的数学函数,表达式中数学函数的写法类似于C\C++语言的写法,数学函数的参数可以是任意表达式,多个参数之间用逗号隔开
 

set x 2;    #变量x的值是2
expr 2* sin($x<3); #表达式的值为1.68294196962

•其中expr是TCL的一个命令,语法为:expr arg ?arg ...?
­ 两个?之间的参数表示可省,后面介绍命令时对于可省参数都使用这种表示形式。

•需要注意的一点是,数学函数并不是命令,只在表达式中出现才有意义,即出现在expr之后才有意义。

List

•list这个概念在TCL中是用来表示集合的。TCL中list是由一堆元素组成的有序集合list可以嵌套定义,list每个元素可以是任意字符串,也可以是list
{}                                                #空 list   
{abed}  {a {b c} d}                      #list可以嵌套

•语法:list  value value...
•这个命令生成list,就是所有的value。
 list 1 2 {3 4} ;                           # list的元素是1 2 {3 4}

•语法:concat list list...             #整合两个list
concat {1 2 3} {4 5 6}                #整合后的list为{1 2 3 4 5 6)

•语法:lindex list index             #返回list的第index个元素
 lindex {1 2 {3 4}} 2                    #返回list的第2个元素3 4

•语法:llength list                     #返回list的元素个数
 llength {1 2 {3 4}}                     #3

•语法:linsert list index value value…  #list插入元素
 linsert {1 2 5 6} 1 7 8                   #1 7 8 2 5 6

• 语法:lappend varname value value...
把每个vaIue的值作为一个元素附加到变量vamame后面
 lappend a 1 2 3      #1 2 3

控制流:if命令

•语法:if test1 body1 else bodyn
TCL先把test1当作一个表达式求值,如果值非0。则把body1当作一个脚本执行并返回所得值,否则把test2当作一个表达式求值,如果值非0,则把body2当作一个脚本执行并返回所得值。

if { $x>0 }{         #注意'{'一定要写在上一行,因为不这样,TCL解释器会认为if命令在换行符已经结束了;
..…                  #同时if和'{'之间应该有空格否则TCL解释器会把if'{'作为一个整体命令导致出错
}elseif {$x==1} {
… } elseif { $ x = = 2 } {  
....}else{
….}

循环命令:while

•语法:while test body
参数test是一个表达式,body是脚本,如果表达式的值非0,就运行脚本,直到表达式为0才停止循环,此while命令中断并返回一个空字符串。此处是一个脚本:

set b " "
set i [expr [llength $a] -1]
while { $i>=0}{
lappend b [lindex $a $i]
incr i-1
)
〃变量a是一个链表,脚本把a的值复制到b

循环变量:for

•语法:for init test reinit body
参数init是一个初始化脚本,第二个参数test是一个表达式 ,用来决定循环什么时候中断,第三个参数reinit是重新初始化的脚本,第四个参数body也是脚本,代表循环体
假设变量a是一个链表,下面的脚本把a的值复制到b:

set b " "
for {set i [expr [llength $a] -1]} {$i>=0} {incr i -1) {
lappend b [lindex $a $i] }
#作用和上面的例子是相同的。

#例如:
for {set i 0} {$i< 10} {incr i} {puts $i;}  #将打印出0到9

foreach命令

• 语法:foreach varName list body
第一个参数varName是一个变量,第二个参数list是一个表(有序集合),第三个参数body是循环体。每次取得链表的元素都会执行循环体一次。

set b " "
foreach i $a{
set b [linsert$b 0 $i]
}

变量a是一个链表,脚本把a的值复制到b。

 switch命令

• 语法:switch  options string ( pattern body  pattern
body ... }
第一个是可选参数options,表示进行匹配的方式。TCL持三种匹配方式:-exact方式,-glob方式,-regexp方式,缺省情况表示-glob方式。-exact方式表示的是精确匹配-glob方式的匹配方式和string match命令的匹配方式相同-regexp方式是正规表达式的匹配方式第二个参数string是要被用来作测试的值,第三个参数是括起来的一个或多个元素对。

switch $x {                   #一旦switch命令找到一个模式匹配
b {incr t1}                   #就执行相应的脚本,并返回脚本的值,作为switch命令的返回值
c {incr t2}                            
}  


switch $x {

           数值1 { 操作1 ;}

             数值2 { 操作2 ;}

          }

                

命令

•在循环体中,可以用break和continue命令中断循环。其中break命令结束整个循环过程并从循环中跳出,continue只是结束本次循环
source命令读一文件并把这个文件的内容作为一个脚本进行求值

source e:/tcl&c/hello.tcl


eval命令是一个用来构造和执行TCL脚本的命令,其语法为:
eval arg arg ...               #它可以接收一个或多个参数,然后把所有的参数以空格隔开组合到一起 成为一个脚本,然后对这个脚本进行求值。
 

eval set a 2 ;set b 4

有关Tcl脚本入门笔记详解(一)的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  3. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  4. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  5. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  6. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  7. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  8. ruby - 确定 ruby​​ 脚本是否已经在运行 - 2

    有没有一种简单的方法可以判断ruby​​脚本是否已经在运行,然后适本地处理它?例如:我有一个名为really_long_script.rb的脚本。我让它每5分钟运行一次。当它运行时,我想看看之前运行的是否还在运行,然后停止第二个脚本的执行。有什么想法吗? 最佳答案 ps是一种非常糟糕的方法,并且可能会出现竞争条件。传统的Unix/Linux方法是将PID写入文件(通常在/var/run中)并在启动时检查该文件是否存在。例如pid文件位于/var/run/myscript.pid然后你会在运行程序之前检查它是否存在。有一些技巧可以避免

  9. ruby - ruby 脚本可以预编译成二进制文件吗? - 2

    我正在开发一个Ruby脚本,需要在没有Ruby解释器的情况下部署到系统上。它将需要在使用ELF格式的FreeBSD系统上运行。我知道有一个ruby​​2exe项目可以编译在Windows上运行的ruby​​脚本,但是在其他操作系统上这样做容易吗?甚至可能吗? 最佳答案 您是否检查过Rubinius或JRuby是否允许您预编译您的代码? 关于ruby-ruby脚本可以预编译成二进制文件吗?,我们在StackOverflow上找到一个类似的问题: https://

  10. ruby-on-rails - Ruby 从 bash 脚本执行中捕获 stderr 输出 - 2

    我目前可以将stdout重定向到ruby​​/rails中的字符串变量,只需在bash中运行命令并将结果设置为我的字符串变量,如下所示。val=%x[#{cmd}]其中cmd是表示bash命令的字符串。但是,这仅捕获stdout,因为我想捕获stderr并将其设置为ruby​​中的字符串——有什么想法吗? 最佳答案 简单地重定向它:val=%x[#{cmd}2>&1]如果您只想从stderr捕获输出,请在将其复制到fd2后关闭stdout的文件描述符。val=%x[#{cmd}2>&1>/dev/null]

随机推荐