cat writebug.cn/history

一个开发者的技术博客。

Tcl 和 expect教程

expect

expect是用于命令行自动化交互的程序,相当于命令行版本的按键精灵。可以用来输入密码,操作程序。

  • expect基于Tcl/Tk实现,在expect脚本中使用Tcl语法和支持Tcl命令。
  • 通过添加Tk,你可以把命令行程序封装在Gui中。
  • O'Reilly提供了一本关于Expect的书,书名为"Exploring Expect: A Tcl-Based Toolkit for Automating Interactive Applications", ISBN 1-56592-090-2.
  • autoexpect 这是expect安装包提供的命令,支持录制expect脚本,录制好后稍微修改就能运行。
  • shebang里面写#!/usr/local/bin/expect −− --的作用是给脚本传递用户参数时避免被当成expect自身的参数。脚本可以从argv里面读取用户传递的参数。
  • 脚本参数在Tcl里面是$argc $argv0 $argv $argc表示参数个数,$argv0相当于C语言中的argv[0], $argv是一个Tcl list, 使用[lindex $argv index]来访问。
  • Tcl 变量用set 变量名 变量值定义。变量用$变量名访问。
  • Tcl if语句通常写法为 if { 条件 } { 条件执行内容 } elseif {条件} {执行内容} else {执行内容}
  • Tcl while语句通常写法为 while { 条件} { 循环体 }
  • Tcl foreach语句通常写法为 foreach 变量名 列表 { 执行内容 }。 while循环和foreach循环支持continue 和break。
常用命令
spawn 启动要交互的应用程序
send 用于向进程发送字符串换行符或者回车符\r 确认输入
send_user 用来打印输出 和tcl中puts差不多
interact 允许用户交互
exit 退出expect脚本
eof expect执行结束
exec 执行程序
sleep 5 暂停5秒


expect 命令作用等待直到其中一个模式与派生进程的输出匹配超时或者遇到文件结尾
expect支持多分支匹配, 用法如下
expect [pat1 body1] ... patn [bodyn]

exp_continue 在expect中多模式匹配就需要用到允许expect本身继续执行而不是按原样返回
set timeout 定义expect超时的时间

一个例子
#!/usr/bin/expect
set timeout 5

expect {
    Password: {
    send_user "password (for $user) on $host: "
    expect_user -re "(.*)\n"
    send_user "\n"
    send "$expect_out(1,string)\r"
    exp_continue
} incorrect {
    send_user "invalid password or account\n"
    exit
} timeout {
    send_user "connection to $host timed out\n"
    exit
} eof {
send_user \
    "connection to host failed: $expect_out(buffer)"
    exit
} -re $prompt

Tcl语言学习

Tcl语言介绍

  • 最早称为“工具命令语言” Tool Command Language
  • 跨平台
  • 容易学习
  • 方便图形界面编程(Tk)
  • 既可以交互式执行脚本,也可以内嵌到其他程序中
  • 支持事件循环
  • 支持package

语法(man Tcl)

  • 分号和换行分隔命令,命令和参数之间用空格/tab分割。#是单行注释,注释字符只有出现在一个命令开始时才有意义。
  • 命令求值过程:1. 解释器把命令分解成字worlds, 完成替换。2. 第一个字是执行过程,后面的字作为执行过程的参数。
  • ""双引号内的内容表示一个“字”, 在引号之间的字符上会进行命令替换、变量替换、和反斜杠替换。
  • {}花括号内的内容表示一个“字”,花括号可以嵌套, 花括号会按照层级进行匹配。在花括号之间的字符上不进行替换,对分号、换行、右方括号、或白空格不做特殊的解释。(除了下面描述的反斜杠-换行替换之外)。字由外侧的花括号之间的字符精确的组成,不包括花括号自身。
  • []方括号之间的内容Tcl进行命令替换。将递归调用Tcl解释器来把方括号中的字符作为一个Tcl脚本处理。脚本的结果将被替换到字方括号的位置。。在一个单一的字中可以有任意数目的命令替换。在由花括号包起来的字上不进行命令替换。
  • $开始的字,Tck会进行变量替换。支持$name ${name}, 替换数组时用$name(index)。index的字符将被进行命令替换、变量替换、和反斜杠替换。${name}可以包含除了右括号之外的任何字符。一个单一的字中可以有任意数目的变量替换。在由花括号包起来的字上不进行命令替换。
  • \反斜线进行转义。支持的转义字符有\a \b \f \n \r \t \v \{ \} \\\<newline>whiteSpace用来替换换行+空格的,整体会被替换为一个空格。
  • \转义符号也支持unicode。\ooo o是最多三个的8进制字符。\xhh h是最多2个的16进制字符。\uhhhh 最多4个的16进制字符。这些会被替换成unicode字符。除了\<newline>whiteSpace,其他在由花括号包起来的字上不进行反斜杠替换。
  • 替换不影响一个命令的字边界。例如,即使变量的值包含空格,在变量替换期间变量的整个的值成为一个单一的字的一部分。

备注

  • 有个交互式解释器tclsh, 可以作为操作系统的shell使用
  • 没有数字类型,可以认为只有Tck中的一切都是字worlds构成。
  • 三种替换,分别是命令替换[], 变量替换$,反斜线替换。

自己瞎点评Tcl语言

  • 函数传参和bash脚本一样,在进入函数前要把列表先展开,然后传递给命令,注定了效率低。
  • 如果没有在参数中展开的变量,比如array这种不支持直接展开的,要在函数内用upvar命令获取上层函数的变量。使用起来不太方便。
  • 不支持类型,一切都是token list,这个特点也让Tcl不能很方便的操作json等带类型的复杂数据结构。比如json里面的"100"是个字符串,转到Tcl里面类型就会丢失和100没区别。而python的json和dict互转就非常自然。
  • 从语言定位来看, Tcl 应该是介于bash script和lua/python之间的编程语言。字符串处理和数据运算比bash方便,调用系统命令方便,也带json数据库网络图形界面支持,适合用来写不太复杂,但又要操作数据或者需要gui的脚本。

常用命令

  • 查看Tcl命令帮助可以man 3Tcl 命令
  • set 变量 值能够设置变量的值, 无需声明变量。
  • unset 变量移除变量
  • puts WORLDS 显示,支持worlds中支持变量替换

条件执行和循环

if根据条件执行脚本。使用方法if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN?。前后有问号的worlds是可以省略的。 if命令把expr1作为一个表达式来求值(用与expr求值它的参数相同的方式)。这个表达式的值必须式一个boolean值(一个数值值,这里0是假而任何其他数值都是真;或者是一个字符串值,比如true或yes是真 而false或no是假);如果它是真通过把body1传递给Tcl解释器来执行它。 由于expr1 和 body1是一个表达式(worlds), 所以一般要用{}或双引号引起来传递给if。不能乱换行,换行需要在{或者"中。 then和else是可选的“噪音词”用来使命令易读。命令的返回值是被执行的那个脚本的返回值。

  • expr命令会首先用空格连接所有参数,在命令提换和变量替换后,计算表达式的值,和C语言差不多。 详见man 3tcl expr
if { $name zhangsan } then {
    puts nihao
} else {
    puts hello
}

if $age >= 14 then "
    puts >=14
" else "
    puts <14
"
if  { 之间要有空格否则会被Tcl当作一个整体
第一个if更易读
这两个区别是用花括号时候 `$name` 是在if外面被求值用双引号则`$age` 在if命令里面被求值替换)。
  • while test body循环。

其他流程控制

foreach
switch

continue break C语言中一样。

字符串处理

  • string命令用于字符串处理

list (man 3tcl list)

  • list ?arg arg ...?这个命令返回由所有arg组成的一个列表。如果未指定arg 则返回一个空串。
list  直接用原始参数来工作。例如,命令
    list a b {c d e} {f {g h}}
将返回
    a b {c d e} {f {g h}}
  • 创建list变量的三种方法set arr "1 2 3 4" set arr {1 2 3 4} set arr [list 1 2 3 4]
  • list相关命令用l开头,可以在tclsh中输入l,查询list相关命令有哪些。lappend lassign lindex linsert list llength lmap load lrange lrepeat lreplace lreverse lsearch lset lsort
  • llength 获取元素个数字符串
  • lrange 截取子list
  • lindex 根据index获取元素
  • linsert 插入
  • lset 修改指定位置的元素
  • lreplace 相当于字符串的replace,可以替换为0个到多个元素。
  • lconat 合并list
  • lappend 追加list元素
  • lrepeat 把value重复N次,得到一个list
  • join 相当于字符串的join。
  • split 把一个字符串分离成一个Tcl列表
  • lsearch 搜索list,支持严格匹配,通配符,或者正则匹配

array数组变量 (man 3tcl array)

  • array set arrayName list创建array,list由偶数个元素组成,每个奇数元素被作为在array中被作为一个index对待。
  • array get arrayName ?pattern? 成对的返回数组元素列表。
  • array size arrayName 返回元素个数字符串
  • glob partten 用于匹配文件路径,支持通配符*?[char]

创建命令(函数)

proc name args body args是一个列表,可以为空,每个元素指定一个参数。每个参数指定符也可以是有一个或两个字段个列表。如果有两个字段,则第一个是参数名而第二个是参数的默认值。

文件操作

常用命令介绍
open
close
file
eof
gets

其他重要的命令

format sprintf格式输出
exec 调用可执行程序
source 执行脚本
info 判断变量是否存在
exit
expr
eval
incr
socket # tcp服务端客户端
encoding
dict
json


man 3tcl namespace
man 3tcl package
ls /usr/share/man/zh_CN/man3/*tcl
rpm -ql tcl
rpm -ql tcllib
rpm -ql tclx