1、Expect简介
Expect是由 Don Libes 基于Tcl语言开发的,并被广泛应用于交互式操作和自动化测试的场景中,它尤其适用于需要对多台服务器执行相同操作的环境中,可以大幅度提高系统管理人员的工作效率。目前,大部分 Unix/Linux 系统安装有expect。万一系统中没有,可以从 http://expect.nist.gov/| f52496c84460745c17c9a9c5df59acb61 | 下载相应的包安装。
Expect作为基于Tcl的高级语言,增加了一些特殊的语法。传统意义上的Expect是以Tcl扩展包的形式出现的,任何Tcl语言编写的应用程序都可以加载Expect功能;此外,Expect已经以模块的方式移植到了Perl和Python语言中,因此用户同样可以在Perl和Python脚本中利用Expect强大的交互功能。
2、Expect语言最基本的命令
send 、 expect 和 spawn 是Expect语言最基本的命令。
send命令会发送字符床给指定进程(process)。
expect命令会等待接受该进程返回的结果并且会根据返回的字符串来决定下一步的操作。
spawn命令可以发起一个进程的运行。
进入expect环境后才可以执行的expect内部命令
$ expect
expect1.1> send ....
expect1.1> expect ....
........
send命令接收一个字符串作为参数并发送给指定的进程从send "hello world"
这行代码中,send会送出字符串 hello world
。如果expec早已经开始与某一个程序进行交互,那么这个字符串将被发送给该程序;而在通常情况下,这个字符串会被送到标准输出设备。
expect命令则等待一个响应,通常是来自于expect正在与之交互的进程,或者来自于标准输入设备;它会等待一个指定的字符串或者满足给定的正则表达式的任何字符串。我们可以创建一个名为response.expect
的文件,来看expect是如何处理的,其内容如下:
#!expect -f
expect "hi"
send "hello there"
终端中运行 $ expect response.expect
,它会等待来自标准输入设备的响应,知道用户输入hi
并回车,它才会发送 hello there
到标准输出设备,并回车。然后结束expect脚本的运行。但是如果用户没有输入hi
并回车,那么expect会继续等待hi
;输入其他的字符并不会影响到expect的工作。通常情况下,expect会一直等待输入,直到最终超时退出。此外,expect还支持使用正则表达式来预防expect匹配到未预想到的输入数据。
spawn命令会调用另一个程序。它的第一个参数要启动程序的名字;剩余的参数则会被传递给该程序作为参数。比如:
expect1.1> spawn ssh username@192.168.1.1
命令会衍生出一个ssh进程,以username用户的身份登陆ip地址为192.168.1.1的电脑中。
用户通过spawn,send和expect这三个基本命令,就可以编写一段expect程序来实现自动化工作。
3、Gentoo下Expect的安装
$ sudo emerge -avt dev-tcltk/expect
4、示例:Expect两个脚本的实现
4.1 通过lftp登陆别人的电脑
当你想通过lftp登陆别人的电脑同步文件时,若只执行lftp sftp:://user@x.x.x.x
登陆了别人的电脑,你会发现其实这只是一种假象,实际上你并没有成功登陆别人的电脑,所以你需先执行ssh user@x.x.x.x
然后在执行上述命令,即可登陆成功。这样做的话,你需要多次输入ip、用户名、密码,很费时间。为了能够提高效率,可以借助expect脚本。过程如下:创建一个ssh.expect的脚本,分别说一下执行脚本时有参数的脚本内容和无参数的脚本内容。
有参数的脚本内容如下:
#!/usr/bin/expect
set timeout 1
set host [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh $host -l $user
expect {
"(yes/no)?" {
send "yes"
expect "Password"
send "$password\r"
}
"Password:" {
send "$password\r"
}
}
send "exit \n"
spawn lftp sftp://$user@$host
expect "Password:"
send "$password\r"
interact
假如你想以xiaomi
的用户名登陆ip
为192.168.0.50
登陆密码为123
的电脑,则在终端执行$ expect ssh.expect 192.168.0.50 xiaomi 123
,终端显示lftp xiaomi@192.168.0.50:~>
。
无参数的脚本内容如下:
#!/usr/bin/expect
set timeout 1
set host 192.168.0.50
set user xiaomi
set password 123
spawn ssh $host -l $user
expect {
"(yes/no)?" {
send "yes"
expect "Password"
send "$password"
}
"Password:" {
send "$password\r"
}
}
send "exit \n"
spawn lftp sftp://$user@$host
expect "Password:"
send "$password\r"
interact
在终端执行$ expect ssh.expect
,终端显示lftp xiaomi@192.168.0.50:~>
。
其中timeout
为send将信息发送给标准输入设备等待执行的时间,你可以修改一下变量值,观察以下效果。
4.2 内核更新
注意send “123\r”
中的123
为你的sudo密码。
#!/usr/bin/expect
set timeout 60
set kernel [lindex $argv 0]
spawn su
expect "Password:"
send "123\r"
expect "#"
send "cd /usr/src/ \n"
expect "#"
send "cp ./linux/.config ./linux-$kernel-gentoo/ \n"
expect "#"
send "rm linux \n"
expect "#"
send "ln -s /usr/src/linux-$kernel-gentoo linux \n"
expect "#"
send "cd linux \n"
expect "#"
send "make -j4 && make modules_install \n"
expect "#"
send "mount /boot && rm /boot/kernel-* \n"
expect "#"
send "cp arch/x86/boot/bzImage /boot/kernel-$kernel-gentoo \n"
expect "#"
send "grub2-mkconfig -o /boot/grub/grub.cfg \n"
expect "#"
send "grub2-install /dev/sda \n"
expect "#"
send "exit \n"
interact
$ expect kernel_renew.expect 4.2.1//4.2.1为你内核新版本号