我接触 Tcl 的起因是最近需要自动化一个基于 Java 的复杂命令行配置实用程序。我做一些使用 Ansible 的自动化编程,偶尔也会使用 expect 模块。坦率地说,我发现这个模块的实用性有限,原因有很多,包括:难以处理相同的提示序列、捕获值以用于其他步骤、控制逻辑的灵活性有限等等。有时你可以使用 shell 模块来代替。但有时你会遇到那些行为不端且过于复杂的命令行界面,似乎不可能自动化。
在我的例子中,我正在自动化安装我们公司的一个程序。最后一个配置步骤只能通过命令行完成,通过几个格式错误、重复的提示和需要捕获的数据输出。传统的 Expect 是唯一的答案。深入理解 Tcl 对于使用 Expect 的基本功能不是必要的,但你了解得越多,你能从中获得的力量就越大。这是一个后续文章的主题。现在,我将探索 Tcl 的基本语言结构,包括用户输入、输出、变量、条件评估、循环和简单函数。
安装 Tcl
在 Linux 系统上,我使用这个
# dnf install tcl
# which tclsh
/bin/tclsh
在 macOS 上,你可以使用 Homebrew 来安装最新的 Tcl
$ brew install tcl-tk
$ which tclsh
/usr/local/bin/tclsh
用 Tcl 猜数字
首先创建基本的脚本执行文件 numgame.tcl
$ touch numgame.tcl
$ chmod 755 numgame.tcl
然后开始在你的文件中编写代码,以通常的 shebang 脚本头开始
#!/usr/bin/tclsh
以下是关于 Tcl 的一些需要注意的点,以便跟随本文进行学习。
第一点是,Tcl 的一切都被视为一系列字符串。变量通常被视为字符串,但可以自动切换类型和内部表示(你通常看不到)。函数可能会将其字符串参数解释为数字 (expr
),并且仅按值传入。字符串通常使用双引号或花括号分隔。双引号允许变量扩展和转义序列,而花括号则完全不进行扩展。
下一点是,Tcl 语句可以用分号分隔,但通常不是必需的。语句行可以使用反斜杠字符分割。但是,通常将多行语句括在花括号中以避免这种情况。花括号更简单,下面的代码格式也反映了这一点。花括号允许延迟评估字符串。在 Tcl 进行变量替换之前,值会传递给函数。
最后,Tcl 使用方括号进行命令替换。方括号之间的任何内容都会被发送到 Tcl 解释器的新递归调用中进行评估。这对于在表达式中间调用函数或为函数生成参数非常方便。
过程
虽然对于这个游戏来说不是必需的,但我从定义 Tcl 函数的示例开始,你可以在以后使用它
proc used_time {start} {
return [expr [clock seconds] - $start]
}
使用 proc
将其设置为函数(或过程)定义。接下来是函数名称。然后是一个包含参数的列表;在本例中是 1 个参数 {start}
,然后是函数体。请注意,函数体的花括号从这行开始,不能在下一行。该函数返回一个值。返回的值是一个复合评估(方括号),它首先读取系统时钟 [clock seconds]
,并进行数学运算以减去 $start
参数。
设置、逻辑和完成
你可以通过一些初始设置、迭代玩家的猜测,然后在完成时打印结果,为这个游戏的其余部分添加更多细节
set num [expr round(rand()*100)]
set starttime [clock seconds]
set guess -1
set count 0
puts "Guess a number between 1 and 100"
while { $guess != $num } {
incr count
puts -nonewline "==> "
flush stdout
gets stdin guess
if { $guess < $num } {
puts "Too small, try again"
} elseif { $guess > $num } {
puts "Too large, try again"
} else {
puts "That's right!"
}
}
set used [used_time $starttime]
puts "You guessed value $num after $count tries and $used elapsed seconds"
第一个 set
语句建立变量。前两个评估表达式以辨别 1 到 100 之间的随机数,下一个保存系统时钟启动时间。
puts
和 gets
命令用于输出到玩家和从玩家输入。我使用的 puts
意味着标准输出。gets
需要定义输入通道,因此此代码指定 stdin
作为来自用户的终端输入的来源。
当 puts
省略行尾终止符时,需要 flush stdout
命令,因为 Tcl 会缓冲输出,并且在需要下一个 I/O 之前可能不会显示输出。
从那里,while
语句说明了循环控制结构和条件逻辑,这些结构和逻辑是给玩家反馈并最终结束循环所必需的。
最后的 set
命令调用我们的函数来计算游戏所用的秒数,然后是收集的统计数据来结束游戏。
玩玩看!
$ ./numgame.tcl
Guess a number between 1 and 100
==> 100
Too large, try again
==> 50
Too large, try again
==> 25
Too large, try again
==> 12
Too large, try again
==> 6
Too large, try again
==> 3
That's right!
You guessed value 3 after 6 tries and 20 elapsed seconds
继续学习
当我开始这个练习时,我怀疑回到 20 世纪 90 年代末流行的语言对我有多大用处。一路上,我发现了一些我非常喜欢的关于 Tcl 的东西——我最喜欢的是方括号命令评估。与许多过度使用复杂闭包结构的语言相比,它看起来更容易阅读和使用。我曾经认为是一种 垂死的语言,实际上仍然在蓬勃发展,并在多个平台上得到支持。我学到了一些新技能,并且对这门古老的语言产生了 Appreciation。
请访问官方网站 https://www.tcl-lang.org。你可以找到最新源代码、二进制发行版、论坛、文档以及仍在进行的会议的信息。
评论已关闭。