SHELL脚本编程-expect
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.expect概述
1>.expect介绍
expect 是由Don Libes基于Tcl( Tool Command Language )语言开发的,主要应用于自动化交互式操作的场景,借助Expect处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率。
2>.expect命令
expect 语法: expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
常用选项 -c:从命令行执行expect脚本,默认expect是交互地执行的 示例:expect -c 'expect " " {send "pressed enter "} -d:可以输出输出调试信息 示例:expect -d ssh.exp
expect中相关命令 spawn:
启动新的进程 send:
用于向进程发送字符串 expect:
从进程接收字符串 interact:
允许用户交互 exp_continue
匹配多个字符串在执行动作后加此命令
3>.安装expect软件包
[root@node101.yinzhengjie.org.cn ~]# yum info expect Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: mirrors.huaweicloud.com * epel: mirrors.aliyun.com * extras: mirrors.huaweicloud.com * updates: mirrors.aliyun.com Available Packages Name : expect Arch : x86_64 Version : 5.45 Release : 14.el7_1 Size : 262 k Repo : base/7/x86_64 Summary : A program-script interaction and testing utility URL : http://expect.nist.gov/ License : Public Domain Description : Expect is a tcl application for automating and testing : interactive applications such as telnet, ftp, passwd, fsck, : rlogin, tip, etc. Expect makes it easy for a script to : control another program and interact with it. : : This package contains expect and some scripts that use it. [root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# yum -y install expect Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile epel/x86_64/metalink | 8.6 kB 00:00:00 * base: mirrors.aliyun.com * epel: mirrors.aliyun.com * extras: ap.stykers.moe * updates: mirrors.aliyun.com base | 3.6 kB 00:00:00 epel | 5.3 kB 00:00:00 extras | 2.9 kB 00:00:00 updates | 2.9 kB 00:00:00 (1/2): epel/x86_64/updateinfo | 1.0 MB 00:00:06 (2/2): epel/x86_64/primary_db | 6.9 MB 00:00:14 Resolving Dependencies --> Running transaction check ---> Package expect.x86_64 0:5.45-14.el7_1 will be installed --> Processing Dependency: libtcl8.5.so()(64bit) for package: expect-5.45-14.el7_1.x86_64 --> Running transaction check ---> Package tcl.x86_64 1:8.5.13-8.el7 will be installed --> Finished Dependency Resolution Dependencies Resolved ============================================================================================================================================================================ Package Arch Version Repository Size ============================================================================================================================================================================ Installing: expect x86_64 5.45-14.el7_1 base 262 k Installing for dependencies: tcl x86_64 1:8.5.13-8.el7 base 1.9 M Transaction Summary ============================================================================================================================================================================ Install 1 Package (+1 Dependent package) Total download size: 2.1 M Installed size: 4.9 M Downloading packages: (1/2): expect-5.45-14.el7_1.x86_64.rpm | 262 kB 00:00:06 (2/2): tcl-8.5.13-8.el7.x86_64.rpm | 1.9 MB 00:00:07 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Total 294 kB/s | 2.1 MB 00:00:07 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : 1:tcl-8.5.13-8.el7.x86_64 1/2 Installing : expect-5.45-14.el7_1.x86_64 2/2 Verifying : 1:tcl-8.5.13-8.el7.x86_64 1/2 Verifying : expect-5.45-14.el7_1.x86_64 2/2 Installed: expect.x86_64 0:5.45-14.el7_1 Dependency Installed: tcl.x86_64 1:8.5.13-8.el7 Complete! [root@node101.yinzhengjie.org.cn ~]#
二.expect最常用的语法(tcl语言:模式-动作)
1>.expect的单分支语法
[root@node101.yinzhengjie.org.cn ~]# expect expect1.1> expect "hi" {send "say hi "} #捕捉用户输入的"hi",然后给用户发送"say hi " hi #这一行是我输入的,由于被我上面定义的语句捕捉到了,下面一行的输出信息就是我之前自定义的字符串信息。 say hi expect1.2> exit #如果不想使用该程序了可以输入"exit"来正常退出交互式界面 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]#
2>.expect的多分支语法
[root@node101.yinzhengjie.org.cn ~]# expect expect1.1> expect "hi" {send "say hi "} +> "bye" {send "byebye "} bye byebye expect1.2> exit [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# expect #匹配hi,hello,bye任意字符串时,执行相应输出。上面的写法和下面的写法功能是相同的。 expect1.1> expect { +> "hi" {send "You said hi "} +> "hehe" {send "hehe yourself "} +> "bye" {send "Good bye "} +> } bye Good bye expect1.2> expect1.2> exit [root@node101.yinzhengjie.org.cn ~]#
三.expect脚本案例实战
1>.spawn(启动新的进程)案例
[root@node101.yinzhengjie.org.cn ~]# ll .ssh/ total 16 -rw-------. 1 root root 413 Jul 9 10:27 authorized_keys -rw-------. 1 root root 1675 Jul 9 10:27 id_rsa -rw-r--r--. 1 root root 413 Jul 9 10:27 id_rsa.pub -rw-r--r--. 1 root root 1608 Jul 9 10:28 known_hosts [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# rm -rf .ssh [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# ll /etc/issue -rw-r--r--. 1 root root 23 Nov 23 2018 /etc/issue [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# ll backup/ total 0 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# cat shell/scp_expect.sh #!/usr/bin/expect #注意,这一行需要写expect命令的执行路径 # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-29 #FileName: shell/scp_expect.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** spawn scp /etc/issue root@172.30.1.101:/root/backup expect { "yes/no" { send "yes ";exp_continue } "password" { send "yinzhengjie " } } expect eof [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# chmod +x shell/scp_expect.sh #添加执行权限 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# ./shell/scp_expect.sh #执行脚本后,我们发现无需像之前那样手动输出"yes"和密码啦~取而代之的是脚本帮咱们干活了。 spawn scp /etc/issue root@172.30.1.101:/root/backup The authenticity of host '172.30.1.101 (172.30.1.101)' can't be established. ECDSA key fingerprint is SHA256:KEchoZnVBkijeoWfG2nvx2ptthsXv7IjkxIJYule57g. ECDSA key fingerprint is MD5:52:c8:f5:6e:5f:cf:44:ec:c4:11:60:d2:d0:31:3c:da. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '172.30.1.101' (ECDSA) to the list of known hosts. root@172.30.1.101's password: issue 100% 23 38.2KB/s 00:00 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# ll backup total 4 -rw-r--r-- 1 root root 23 Nov 29 19:21 issue [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]#
2>.interact(允许用户交互)案例
[root@node101.yinzhengjie.org.cn ~]# cat shell/ssh_expect1.sh #!/usr/bin/expect # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-29 #FileName: shell/scp_expect.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** spawn ssh yinzhengjie@172.30.1.101 expect { "yes/no" { send "yes ";exp_continue } "password" { send "yinzhengjie " } } interact [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# ./shell/ssh_expect1.sh spawn ssh yinzhengjie@172.30.1.101 yinzhengjie@172.30.1.101's password: Last login: Sat Nov 30 17:05:32 2019 from 172.30.1.101 [yinzhengjie@node101.yinzhengjie.org.cn ~]$ [yinzhengjie@node101.yinzhengjie.org.cn ~]$ who yinzhengjie pts/1 2019-11-30 17:06 (172.30.1.101) root pts/3 2019-11-29 08:24 (172.30.1.254) root pts/2 2019-11-29 08:24 (172.30.1.254) [yinzhengjie@node101.yinzhengjie.org.cn ~]$ [yinzhengjie@node101.yinzhengjie.org.cn ~]$ exit logout Connection to 172.30.1.101 closed. [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]#
3>.变量使用案例
[root@node101.yinzhengjie.org.cn ~]# cat shell/ssh_expect2.sh #!/usr/bin/expect # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-29 #FileName: shell/scp_expect.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** set host 172.30.1.101 set user yinzhengjie set password yinzhengjie spawn ssh ${user}@${host} expect { "yes/no" { send "yes ";exp_continue } "password" { send "${password} " } } interact [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# ./shell/ssh_expect2.sh spawn ssh yinzhengjie@172.30.1.101 yinzhengjie@172.30.1.101's password: Last login: Sat Nov 30 17:09:58 2019 from 172.30.1.101 [yinzhengjie@node101.yinzhengjie.org.cn ~]$ [yinzhengjie@node101.yinzhengjie.org.cn ~]$ whoami yinzhengjie [yinzhengjie@node101.yinzhengjie.org.cn ~]$ [yinzhengjie@node101.yinzhengjie.org.cn ~]$ exit logout Connection to 172.30.1.101 closed. [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]#
4>.位置参数案例
[root@node101.yinzhengjie.org.cn ~]# cat shell/ssh_expect3.sh #!/usr/bin/expect # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-29 #FileName: shell/scp_expect.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** set host [lindex $argv 0] set user [lindex $argv 1] set password [lindex $argv 2] spawn ssh ${user}@${host} expect { "yes/no" { send "yes ";exp_continue } "password" { send "${password} " } } interact [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# ./shell/ssh_expect3.sh 172.30.1.101 yinzhengjie yinzhengjie spawn ssh yinzhengjie@172.30.1.101 yinzhengjie@172.30.1.101's password: Last login: Sat Nov 30 17:10:27 2019 from 172.30.1.101 [yinzhengjie@node101.yinzhengjie.org.cn ~]$ [yinzhengjie@node101.yinzhengjie.org.cn ~]$ whoami yinzhengjie [yinzhengjie@node101.yinzhengjie.org.cn ~]$ [yinzhengjie@node101.yinzhengjie.org.cn ~]$ exit logout Connection to 172.30.1.101 closed. [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]#
5>.登录后执行多个命令案例
[root@node101.yinzhengjie.org.cn ~]# cat shell/ssh_expect4.sh #!/usr/bin/expect # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-29 #FileName: shell/scp_expect.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** set host [lindex $argv 0] set user [lindex $argv 1] set password [lindex $argv 2] set createuser [lindex $argv 3] set createpasswd [lindex $argv 4] spawn ssh ${user}@${host} expect { "yes/no" { send "yes ";exp_continue } "password" { send "${password} " } } expect "]#" { send "useradd ${createuser} " } expect "]#" { send "echo ${createpasswd} |passwd --stdin ${createuser} " } expect "]#" { send "who " } send "exit " expect eof [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# who root pts/3 2019-11-29 08:24 (172.30.1.254) root pts/2 2019-11-29 08:24 (172.30.1.254) [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# id jason id: jason: no such user [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# ./shell/ssh_expect4.sh 172.30.1.101 root yinzhengjie jason 123456 spawn ssh root@172.30.1.101 root@172.30.1.101's password: Last login: Sat Nov 30 17:23:11 2019 from 172.30.1.101 [root@node101.yinzhengjie.org.cn ~]# useradd jason [root@node101.yinzhengjie.org.cn ~]# echo 123456 |passwd --stdin jason Changing password for user jason. passwd: all authentication tokens updated successfully. [root@node101.yinzhengjie.org.cn ~]# who root pts/1 2019-11-30 17:27 (172.30.1.101) root pts/3 2019-11-29 08:24 (172.30.1.254) root pts/2 2019-11-29 08:24 (172.30.1.254) [root@node101.yinzhengjie.org.cn ~]# exit logout Connection to 172.30.1.101 closed. [root@node101.yinzhengjie.org.cn ~]# who root pts/3 2019-11-29 08:24 (172.30.1.254) root pts/2 2019-11-29 08:24 (172.30.1.254) [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# id jason uid=1049(jason) gid=1049(jason) groups=1049(jason) [root@node101.yinzhengjie.org.cn ~]#
6>.shell脚本调用expect
[root@node101.yinzhengjie.org.cn ~]# cat shell/ssh_expect5.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-30 #FileName: shell/ssh_expect5.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** ip=$1 user=$2 password=$3 expect <<EOF set timeout 10 spawn ssh $user@$ip expect { "yes/no" { send "yes ";exp_continue } "password" { send "$password " } } expect "]#" { send "useradd jason2019 " } expect "]#" { send "echo yinzhengjie |passwd --stdin jason2019 " } expect "]#" { send "who " } expect "]#" { send "exit " } expect eof EOF [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# who root pts/3 2019-11-29 08:24 (172.30.1.254) root pts/2 2019-11-29 08:24 (172.30.1.254) [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# id jason2019 id: jason2019: no such user [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# bash shell/ssh_expect5.sh 172.30.1.101 root yinzhengjie spawn ssh root@172.30.1.101 root@172.30.1.101's password: Last login: Sat Nov 30 17:27:57 2019 from 172.30.1.101 [root@node101.yinzhengjie.org.cn ~]# useradd jason2019 [root@node101.yinzhengjie.org.cn ~]# echo yinzhengjie |passwd --stdin jason2019 Changing password for user jason2019. passwd: all authentication tokens updated successfully. [root@node101.yinzhengjie.org.cn ~]# who root pts/1 2019-11-30 17:38 (172.30.1.101) root pts/3 2019-11-29 08:24 (172.30.1.254) root pts/2 2019-11-29 08:24 (172.30.1.254) [root@node101.yinzhengjie.org.cn ~]# exit logout Connection to 172.30.1.101 closed. [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# id jason2019 uid=1050(jason2019) gid=1050(jason2019) groups=1050(jason2019) [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# who root pts/3 2019-11-29 08:24 (172.30.1.254) root pts/2 2019-11-29 08:24 (172.30.1.254) [root@node101.yinzhengjie.org.cn ~]#
7>.模拟给集群批量创建用户的脚本
[root@node101.yinzhengjie.org.cn ~]# cat shell/ssh_expect6.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-30 #FileName: shell/ssh_expect5.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** while read ip ;do user=root password=yinzhengjie expect <<EOF set timeout 30 spawn ssh $user@$ip expect { "yes/no" { send "yes ";exp_continue } "password" { send "$password " } } expect "]#" { send "useradd hadoop " } expect "]#" { send "echo yinzhengjie |passwd --stdin hadoop " } expect "]#" { send "who " } expect "]#" { send "exit " } expect eof EOF done < /root/shell/iplist.txt [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# cat shell/iplist.txt #咱们这里定义了需要连接集群的主机IP 172.30.1.101 172.30.1.102 172.30.1.103 172.30.1.104 172.30.1.105 172.30.1.106 172.30.1.107 172.30.1.108 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# bash shell/ssh_expect6.sh #执行脚本后发现会有很多输出信息,由于我试验的时候之开机了172.30.1.101这台虚拟机,其它虚拟机全部关机了,结果脚本只在开机的虚拟机中执行成功啦,其它主机报错提示找不到主机路由。 spawn ssh root@172.30.1.101 root@172.30.1.101's password: Last login: Sat Nov 30 17:38:48 2019 from 172.30.1.101 [root@node101.yinzhengjie.org.cn ~]# useradd hadoop [root@node101.yinzhengjie.org.cn ~]# echo yinzhengjie |passwd --stdin hadoop Changing password for user hadoop. passwd: all authentication tokens updated successfully. [root@node101.yinzhengjie.org.cn ~]# who root pts/1 2019-11-30 17:53 (172.30.1.101) root pts/3 2019-11-29 08:24 (172.30.1.254) root pts/2 2019-11-29 08:24 (172.30.1.254) [root@node101.yinzhengjie.org.cn ~]# exit logout Connection to 172.30.1.101 closed. spawn ssh root@172.30.1.102 ssh: connect to host 172.30.1.102 port 22: No route to host expect: spawn id exp6 not open while executing "expect "]#" { send "useradd hadoop " }" spawn ssh root@172.30.1.103 ssh: connect to host 172.30.1.103 port 22: No route to host expect: spawn id exp6 not open while executing "expect "]#" { send "useradd hadoop " }" spawn ssh root@172.30.1.104 ssh: connect to host 172.30.1.104 port 22: No route to host expect: spawn id exp6 not open while executing "expect "]#" { send "useradd hadoop " }" spawn ssh root@172.30.1.105 ssh: connect to host 172.30.1.105 port 22: No route to host expect: spawn id exp6 not open while executing "expect "]#" { send "useradd hadoop " }" spawn ssh root@172.30.1.106 ssh: connect to host 172.30.1.106 port 22: No route to host expect: spawn id exp6 not open while executing "expect "]#" { send "useradd hadoop " }" spawn ssh root@172.30.1.107 ssh: connect to host 172.30.1.107 port 22: No route to host expect: spawn id exp6 not open while executing "expect "]#" { send "useradd hadoop " }" spawn ssh root@172.30.1.108 ssh: connect to host 172.30.1.108 port 22: No route to host expect: spawn id exp6 not open while executing "expect "]#" { send "useradd hadoop " }" [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# id hadoop #很显然,只有172.30.1.101主机命令执行成功了。 uid=1051(hadoop) gid=1051(hadoop) groups=1051(hadoop) [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]#