免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 67346 | 回复: 61

[学习共享] expect FAQ [复制链接]

论坛徽章:
16
IT运维版块每日发帖之星
日期:2015-08-24 06:20:00综合交流区版块每日发帖之星
日期:2015-10-14 06:20:00IT运维版块每日发帖之星
日期:2015-10-25 06:20:00IT运维版块每日发帖之星
日期:2015-11-06 06:20:00IT运维版块每日发帖之星
日期:2015-12-10 06:20:00平安夜徽章
日期:2015-12-26 00:06:302016猴年福章徽章
日期:2016-02-18 15:30:34IT运维版块每日发帖之星
日期:2016-04-15 06:20:00IT运维版块每日发帖之星
日期:2016-05-21 06:20:00综合交流区版块每日发帖之星
日期:2016-08-16 06:20:002015七夕节徽章
日期:2015-08-21 11:06:17IT运维版块每日发帖之星
日期:2015-08-14 06:20:00
发表于 2011-07-15 09:46 |显示全部楼层
本帖最后由 expert1 于 2015-11-03 09:45 编辑

firstly,版权没有,随意COPY && PASTE,但注意注明出处即可。

前言,这里讲Expect的只言片语,具体要了解需要熟悉TCL语言的相关知识。这里举了最常用的问题,就系统管理而言,足以满足日常需求,若是你做软件自动化测试。要深入了解expect,请下载http://bbs.chinaunix.net/thread-1769951-1-1.html此外还需要熟悉TCL,毕竟它和TCL相关。
一,expect的FAQ
如何匹配多种情况,典型的例子就是ssh ,第一个可能是yes,然后password,或是直接password。就是并行匹配的情况,见下面例子。
  1. #!/usr/bin/expect
  2.     set timeout 60
  3.     set pwd "该机器的密码"
  4.     spawn ssh  10.10.10.1
  5.            expect {
  6.                       "\[#$\]"     {send "\r" }   ### 假如有了ssh 公钥之类的,直接回车。当然普通用户下边还可能需要sudo,自己处理一下吧。
  7.                     "not know" {send_user "[exec echo \"not know\"]";exit}

  8.                     "(yes/no)?" {send "yes\r";exp_continue} #continue的意义,靠猜测也能差不多知道了吧?可以Man expect

  9.                     "password:" {send  "$pwd\r"}

  10.                     "Permission denied, please try again." {

  11.                         send_user "[exec echo \"Error:Password is wrong\"]"

  12.                         exit  }

  13.                     }
复制代码
####  把所有可能出现的情况列举出来做匹配,假如写法如下就是串行执行了。
expect "*#"
send "ifconfig\r"
send "exit\r"
expect eof # 只有spawn产生的进程的相关信息才能被expect捕捉到,还包含2个特殊情况,eof和timeout,eof关闭spawn 产生的spawn id :exp_id,也就是结束标记。这个eof是必不可少的,至于为什么,我也没找到权威答案,包括手册都没提到,但实践中发现很多时候不加会导致得不到你要的结果。请看下边的一个例子:
  1. #!/usr/bin/expect -f

  2. set ip 10.1.1.1
  3. set pwd 123456

  4. spawn scp ssh.exp root@$ip:/tmp

  5. expect {
  6.          "(yes/no)?" {send "yes\r";exp_continue}

  7.          "password:" {send  "$pwd\r"}
  8.                     }

  9. # expect eof
复制代码
假如最后的expect eof给注释了,文件不会被scp到10.1.1.1上,也许你感觉很奇怪,关键就在于这个eof,我看了相关资料包括debug信息没有找到令人信服的答案。
我个人猜测是,没有eof,那么仅仅是把密码send过去,连回车都没有执行。然后就异常退出了。加了eof部分,让Expect执行完毕,下边才能退出子程序。

注意set timeout 这个要保证你下边的command能完成
比如你scp一个文件需要30s,而你set timeout 10那么会超时退出的。

其他自己看tcl和expect的manual吧,假如你想把ip,密码放文本里,也可以,只需要set 参数,具体到我博客看,我懒得贴了。大致是
set name [lindex $argv 0],或看下边的例子。

------------------------------------------------------------------------------
添加一个mysql,实际上是expect的interact的例子
#!/usr/bin/expect

set pwd 123456
spawn mysql -uroot -p

expect "password"
send "$pwd\r"
expect "mysql>"
send "show databases;\r"
interact

这样控制权交给了mysql命令行,你可以手工执行各种操作了。有的时候可能需要这个。
## for 循环一例
改密码,for循环的sample
spawn passwd test
for {set i 1} {$i<=2} {incr i} {
expect "password:"
send "1\#23abc\r"
}
expect eof
=============================================================

注意expect的特殊情况,需要转义,仅仅列举一些例子。
比如expect "#"
send "ps -ef|grep cpusd|grep -v $$ |awk '{print \"kill -9 \"\$2}'|sh\r"
杀死远程机器上cpusd进程,awk后边的"和$2都要\转义,否则$2是一个变量会报错。“转义是因为开头有了",会认为extra close-quota.
set定义的变量也是如此,比如你set pwd 123\abc ,那么应该写成set pwd 123\\abc来转义。

如何得到命令的结果?
有时候你send一个命令,希望得到结果,并加以判断,需求在这里http://bbs.chinaunix.net/thread-3575650-1-1.html
你可以看下expect_out(buffer)这个buffer保存了上一个命令输出的到匹配处之间的输出,可以通过正则来提取出来。
send "ip addr|awk '/eth0/&&/32/{print \$2}'\r" #执行一个shell命令,看结果是否是172.10.0.249/32
expect -re {.*\/(.*)\r\n} {  #可以用正则的子串来提取命令执行结果,expect_out(0,string)是整个正则匹配的内容,1是第一个子串匹配的内容,有趣的是,包括你send过去的命令连同shell 提示符号[root@test.com~]# 也被被当做输出。这个可以通过debug看出来,回车符号是\r\n。至于子串匹配,可以看手册,一共有9个,当然第二个就是expect_out(2,string),以此类推。
set ip $expect_out(1,string)   把这个子串正则匹配的结果赋给IP
}

if {$ip == "172.18.0.249/32" } { #判断是否,做动作。
send "who\r" }

如何获取一个本地的shell,在expect里使用?
其实这个在上边已经出现过了,只需要exec即可。比如获取本地的IP地址
set ip [exec shell_command]


如何关闭日志?log_user 0,当然你可以在某个阶段关闭,某个阶段开始,log_user 1开启。所有的输出记录在expect_out(buffer)和send过去的command都被关闭。如何记录日志?log_file 日志名字。具体看手册

如何远程执行本地写好的脚本?需求见。http://bbs.chinaunix.net/viewthr ... 2585&from=favorites
利用tcl文件操作知识,把脚本内容读取赋给cmd,然后send过去。

open urscript [ open A.sh ]
while {[ gets $urscript cmd ] >= 0} {send "$cmd\r"}
close $urscript

具体请后边连接,但不够通用,比如脚本需要带上参数这个办法可能就不太好了
如何把ip,密码写文本里,然后批量执行?请看下边的sample(ssh.exp)
  1. set f [open ip r]
  2. while { [gets $f line ]>=0 } {

  3.     set ip [lindex $line 0]
  4.     set pwd [lindex $line 1]
  5.     spawn ssh $ip

  6.        expect {
  7.                     "not know" {send_user "[exec echo \"not know\"]";exit}

  8.                     "(yes/no)?" {send "yes\r";exp_continue}

  9.                     "password:" {send  "$pwd\r"}

  10.                     "Permission denied, please try again." { send_user "[exec echo \"Error:Password is wrong\"]"
  11.                                                               
  12.                                                        exit  }
  13.                 }
  14. expect "#"
  15. send "ifconfig\r"
  16. expect "#"
  17. send "exit\r"
  18. expect eof
  19.               
  20.          }
  21. close $f
复制代码
其中ip里放的是ip ,密码,只需要执行expect -f ssh.exp 即可。这个open,close完全是tcl的文件操作知识。

二,高级话题,你真的懂expect吗?它是如何工作的?为何有诡异问题出现?为何有时候取不到send过去的命令的执行结果??------有空写!

三,一个批量执行任务的python脚本(IT民工的福音啊

民工们一般维护同类的机器很多,有的时候需要所有机器都做一个操作,比如20台机器都要安装mysql之类的,最开始的时候咋处理?稍微聪明点的办法,写安装脚本,调试测试OK,一个个登陆上去执行。或是安装好了打个包,然后用脚本替换配置文件等。还要20台一个个登陆,万一机器多到50,100台这种事枯燥而且易出错,真是民工的活啊。在好友的帮助下,经我反复测试写了一个Mssh.py(multipart-job ssh),适用于以上场景。
简单说明:Linux 平台,python >=2.6(多数linux版本包括不限于RHEL5,CENTOS5都是2.4.3,哪怕你yum 升级也是2.4.3,需要额外安装2.6,因为需要多进程的模块,而2.6以后才有这个模块)
另外需要安装pexpect(一个expect-like)的模块,假如yum安装的话,需要拷贝/usr/lib/python2.4/site-packages/pexpect.py到2.6的目录下/usr/local/lib/python2.6/site-packages/,否则会报错,没有这个模块,如果自己下载安装,我没试过,可能也类似需要放到这个目录下。

说明,1.假如机器允许root登录,此时把需要登录的ip password放一个文件hostlist里,格式
ip password
比如10.10.10.1 123abc
可以写注释,以#开头
比如:
#nginx host
10.10.10.1 123456
。。。
但是不能有空行,否则会报错。
比如:
10.10.10.1
这里不能有空行
10.10.1.2
----------------
2.禁止root登录,只允许普通用户,假如是test用户登录,然后sudo。
那么hostlist格式
ip test用户的密码 sudo到root的密码
比如:
10.1.1.1 123456 superpwd
同样不能有空行,可以加#注释

更新:删除了之前发的python脚本,更新至最新的多进程测试完毕的版本,考虑到了root登陆,非root然后sudo,要输入yes,passwd,直接passwd,ssh信任(无密码和很BT的有密码的2种),端口非22等各种特殊情况。除了ssh信任key未测外,其他都测试完毕。测试ssh key有一些问题,在改动特别感谢好友jiaion给的帮助

首先在机器上mkdir /tmp/mssh/

为了防止粘贴变形,把脚本内容放在附件里。

测试一下:python mssh.py,会出现提示
  1. Usage: mssh.py -f hostlist -u user -c "cmd" versrion 1.1

  2. Options:
  3. --version show program's version number and exit
  4. -h, --help show this help message and exit
  5. -f FILE, --file=FILE host list ,which stores ip and password
  6. -u USER, --user=USER username,root or other users
  7. --port=PORT sshd's port ,default:22
  8. -d, --debug Output debug messages
  9. -c CMD, --cmd=CMD commadn to be exected,don't forget to type "", e.g
  10. "ifconfig"
复制代码
完整的用法,python mssh.py -f hostlist -u root -c "ifconfig" --port 22 -d
建议执行命令要加上“”,比如"ifconfig"
其中--port 22可以不加,但port非22要--port portnum 比如--port 51223
-d 观察整个执行过程的输出,debug用,但建议慎用,因为多进程,信息输出打乱了。
看似只能执行简单的ifconfig,好吧,给你个好办法,把你的脚本调试完毕,放到一个http server上,我一般建议放监控(cacti)上,路由最全,而且肯定网络通,为啥,自己想去。然后执行批量下载命令
python mssh.py -u root -c "wget http://ip/scrpit.sh"
然后执行python mssh.py -u root -c "run scrpit.sh"
多爽啊,几十台机器都做一个事,你需要的仅仅是写好各种脚本即可,就看你的脚本能力了。
假如你熟悉python,完全可以把执行的脚本或命令放到本地,然后read整个内容,赋值给cmd,然后send过去,但这种有很大的弊端,不够通用。
以上对非root也可以,python mssh.py -u test -c 。。。
特别注意:
不要在执行的命令里出现交互的情况,除非你能解决这个交互。比如python mssh.py -u root -c "yum install expect" 这个结果会等待到超时抛出异常,因为有个y/n,让你输入,你要明确指定yum -y install expect
第二,你的执行机器上的环境变量直接决定了脚本是否能执行成功,比如你python mssh.py -u root -c "command",而这个命令没在$PATH里,那肯定是报错的.
第三看,不要写错ip,password,sudopwd,否则。。。

最后无-d 参数,整个执行日志记录在/tmp/mssh/$ip,以IP为日志名字,是追加的。


了减轻广大的民工敲键盘的负担,耗费一个多星期反复测试的东西,请随意COPY,只要注明出处即可]。Enjoy it
也希望能替代puppet,cfengine之类的(这种原理大同小异,除了安装维护需费时费力外,还需要熟悉模板的描写,加上ZH_CN中文资料甚少。。。)

mssh.py.tar.bz2

2.1 KB, 下载次数: 505

论坛徽章:
16
IT运维版块每日发帖之星
日期:2015-08-24 06:20:00综合交流区版块每日发帖之星
日期:2015-10-14 06:20:00IT运维版块每日发帖之星
日期:2015-10-25 06:20:00IT运维版块每日发帖之星
日期:2015-11-06 06:20:00IT运维版块每日发帖之星
日期:2015-12-10 06:20:00平安夜徽章
日期:2015-12-26 00:06:302016猴年福章徽章
日期:2016-02-18 15:30:34IT运维版块每日发帖之星
日期:2016-04-15 06:20:00IT运维版块每日发帖之星
日期:2016-05-21 06:20:00综合交流区版块每日发帖之星
日期:2016-08-16 06:20:002015七夕节徽章
日期:2015-08-21 11:06:17IT运维版块每日发帖之星
日期:2015-08-14 06:20:00
发表于 2011-07-19 14:27 |显示全部楼层
为啥要添加sudo?有的场合,禁止了root直接登录,只能普通用户登录,此时就只能sudo了,普通用户权限太小了。

论坛徽章:
0
发表于 2011-07-22 17:06 |显示全部楼层
本帖最后由 Cloud.D.Ace 于 2011-07-22 17:07 编辑

回复 1# expert1

谢谢楼主分享。
建议再加上No route to host,即主机连不上这种情况的判断

论坛徽章:
16
IT运维版块每日发帖之星
日期:2015-08-24 06:20:00综合交流区版块每日发帖之星
日期:2015-10-14 06:20:00IT运维版块每日发帖之星
日期:2015-10-25 06:20:00IT运维版块每日发帖之星
日期:2015-11-06 06:20:00IT运维版块每日发帖之星
日期:2015-12-10 06:20:00平安夜徽章
日期:2015-12-26 00:06:302016猴年福章徽章
日期:2016-02-18 15:30:34IT运维版块每日发帖之星
日期:2016-04-15 06:20:00IT运维版块每日发帖之星
日期:2016-05-21 06:20:00综合交流区版块每日发帖之星
日期:2016-08-16 06:20:002015七夕节徽章
日期:2015-08-21 11:06:17IT运维版块每日发帖之星
日期:2015-08-14 06:20:00
发表于 2011-07-23 11:39 |显示全部楼层
回复 9# Cloud.D.Ace


    这个是啥情况呀,我从没遇到过,你把当时的输出贴出来我更正一下,让它更完美一点呵呵。

论坛徽章:
0
发表于 2011-07-25 14:47 |显示全部楼层
本帖最后由 Cloud.D.Ace 于 2011-07-25 14:52 编辑
回复  Cloud.D.Ace


    这个是啥情况呀,我从没遇到过,你把当时的输出贴出来我更正一下,让它更完美 ...
expert1 发表于 2011-07-23 11:39



    就是一堆机器批量进行操作,但是有些机器死机或者关机了,就会出现这种情况。
测试很容易,你只要随便写个不存在的ip就可以了

以下是输出:
ssh:connect to host 192.168.1.2 port 22: No route to host
貌似还有种是:
ssh:connect to host 192.168.1.255 port 22: Network is unreachable

论坛徽章:
0
发表于 2011-07-25 17:39 |显示全部楼层
  1. #!/usr/bin/expect -f
  2. if {$argc != 2} {
  3.     puts stderr "Usage:remotehost command \n"
  4.     exit 1
  5. }
  6. set HOST [lindex $argv 0]
  7. set PASSWD "passwordinputhere"
  8. set COMMAND [lindex $argv 1 ]
  9. send_user "connecting to $HOST...\n"
  10. spawn ssh $HOST
  11. set done 1
  12. while {$done} {
  13.         expect {
  14.             "*No route to host" {
  15.                 send_user "Host DOWN!\n"
  16.                 close
  17.                 exit 1
  18.             }
  19.             "*(yes/no)?" {send "yes\n"}
  20.             "?assword:" {send "$PASSWD\n"}
  21.             "*Permission denied*" {
  22.                 send_user "password incorrect!\n"
  23.                 close
  24.                 exit 1
  25.             }
  26.             "*Connection refused*" {
  27.                 send_user "connection refused!\n"
  28.                 close
  29.                 exit 1
  30.             }
  31.             "*#" {
  32.                 send "$command\n"
  33.                 close
  34.                 exit 0
  35.             }
  36.         }
  37. }
复制代码
顺便帮我看看为什么我上面的脚本执行达不到我要的效果啊,谢谢!
脚本参数:host对应主机,command对应命令。
我这边有些机器加了key可以直接登录的。

论坛徽章:
16
IT运维版块每日发帖之星
日期:2015-08-24 06:20:00综合交流区版块每日发帖之星
日期:2015-10-14 06:20:00IT运维版块每日发帖之星
日期:2015-10-25 06:20:00IT运维版块每日发帖之星
日期:2015-11-06 06:20:00IT运维版块每日发帖之星
日期:2015-12-10 06:20:00平安夜徽章
日期:2015-12-26 00:06:302016猴年福章徽章
日期:2016-02-18 15:30:34IT运维版块每日发帖之星
日期:2016-04-15 06:20:00IT运维版块每日发帖之星
日期:2016-05-21 06:20:00综合交流区版块每日发帖之星
日期:2016-08-16 06:20:002015七夕节徽章
日期:2015-08-21 11:06:17IT运维版块每日发帖之星
日期:2015-08-14 06:20:00
发表于 2011-07-26 09:53 |显示全部楼层
本帖最后由 expert1 于 2011-08-08 20:48 编辑

回复 12# Cloud.D.Ace


send后边,即使你做了ssh,也可以这么来
spawn ssh $host

expect {
  
"[\$\#]" { 因为做了ssh信任,这里就直接是$或#,直接发一个回车,但有的时候很bt,做了ssh信任,还设了密码,那么就应该是输入密码了,然后sudo su -之类的,这个看情况而定了。

论坛徽章:
0
发表于 2011-07-26 10:05 |显示全部楼层
回复 13# expert1


    是啊,我想在一个脚本里面处理这种直接ssh就可以登录的,和需要输入密码的这2种情况。
请教版主应该怎么修改呢?

论坛徽章:
16
IT运维版块每日发帖之星
日期:2015-08-24 06:20:00综合交流区版块每日发帖之星
日期:2015-10-14 06:20:00IT运维版块每日发帖之星
日期:2015-10-25 06:20:00IT运维版块每日发帖之星
日期:2015-11-06 06:20:00IT运维版块每日发帖之星
日期:2015-12-10 06:20:00平安夜徽章
日期:2015-12-26 00:06:302016猴年福章徽章
日期:2016-02-18 15:30:34IT运维版块每日发帖之星
日期:2016-04-15 06:20:00IT运维版块每日发帖之星
日期:2016-05-21 06:20:00综合交流区版块每日发帖之星
日期:2016-08-16 06:20:002015七夕节徽章
日期:2015-08-21 11:06:17IT运维版块每日发帖之星
日期:2015-08-14 06:20:00
发表于 2011-07-26 10:24 |显示全部楼层
本帖最后由 expert1 于 2011-07-26 10:38 编辑

回复 14# Cloud.D.Ace


    看13楼,已经给了例子,做了ssh trust key,匹配了#或$,看你的用户是否是root了,假如BT一点,还可能遇到需要密码的,你比葫芦画瓢写就可以了。可能写的比较繁琐,需要耐心,写好了贴出来大家共享学习一下啊呵呵。

论坛徽章:
0
发表于 2011-07-26 11:19 |显示全部楼层
本帖最后由 Cloud.D.Ace 于 2011-07-26 11:21 编辑

奇怪,我怎么匹配不了$
用expect  '$' 或者"$"都不行
一连上就在那不动了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP