expect

expect是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。

expect自动交互流程:
spawn启动指定进程—expect获取指定关键字—send向指定程序发送指定字符—执行完成退出.

expect 参数
spawn               交互程序开始后面跟命令或者指定程序
expect              获取匹配信息匹配成功则执行expect后面的程序动作
send exp_send       用于发送指定的字符串信息
exp_continue        在expect中多次匹配就需要用到
send_user           用来打印输出 相当于shell中的echo
exit                退出expect脚本
eof                 expect执行结束 退出
set                 定义变量
puts                输出变量
set timeout         设置超时时间

1、在linux系统中/root/目录下有一个文件ip.ini,内容如下:

192.168.149.131,root,nihao123!,
192.168.149.132,root,nihao123@

每一行的格式分别为linux服务的IP 用户 密码,用一个shell脚本批量查看着看这些服务器的主机名;
脚本代码如下: 一个expect.sh脚本 一个exe.sh脚本,用来调用expect.sh

[root@localhost_002 shell]# cat expect.sh 
#!/usr/bin/expect -f
set host [lindex $argv 0]		# 接收第1个参数,作为IP
set use [lindex $argv 1]		# 接收第2个参数,作为use
set passwd [lindex $argv 2]		 # 接收第3个参数,作为密码
spawn ssh $use@$host			# 发送ssh请求
expect {			# 返回信息匹配
"*yes/no" { send "yes\r"; exp_continue }		# 第一次ssh连接会提示yes/no,继续
"*password:" { send "$passwd\r" } 			# 出现密码提示,发送密码
expect "#*"
send "hostname\r"
expect "#*"
send "exit\r"
[root@localhost_002 shell]# cat exe.sh 
#!/bin/bash
cat ip.ini|while read line
do
    ip=`echo $line |awk -F ',' '{print $1}'`
    ur=`echo $line |awk -F ',' '{print $2}'`
    pw=`echo $line |awk -F ',' '{print $3}'`
    ./expect.sh  $ip $ur $pw
done
执行代码:
[root@localhost_002 shell]# sh exe.sh 
spawn ssh -p 52588 root@192.168.149.131
Last login: Sun Mar 10 12:14:56 2019 from 192.168.149.130
[root@localhost_03 ~]# hostname

localhost_03

expect是一种脚本语言,通过它可以实现传输,上线代码,可以实现远程执行命令,不需要输入密码

首先准备一台模板机器(最新的代码待上线),然后给下面分发代码的多台台机器上线,机器的IP,对应用户的密码,然通过expect,借助rsync来上传代码,可以用expect去登录执行某些命令:

1:expect脚本安装:

yum   install   -y    expect     (安装包和mkpasswd是一起的)

示例1:自动远程登录,并执行一些命令:

[root@localhost_01 sbin]# vim 1.expect  
#! /usr/bin/expect
set host "192.168.149.129"
set passwd "nihao123!"
set port "-p 56888"
spawn ssh $port root@$host
expect {
"yes/no" { send "yes\r"; exp_continue}  
"password:" { send "$passwd\r" }
}
##expect eof  表示登录远程机器后,暂停一两秒钟后退出:
interact  

注释:在expect中定义变量需要set host才可以:
注释:在文件里 expect { }这里是保证登录信息,第一次登录时候,则需要yes记录登录信息,下一次则不需要了,需要清空 >/root/.ssh/known_hosts才可以:
exp_continue表示继续/r换行, interact表示继续停留在这台机器不退出:如果不加的话则登录后马上退出:

[root@localhost_01 sbin]# ssh -p 56888 192.168.149.129 
root@192.168.149.129's password: 
#因为我们以前登录过,所有不询问为了安全性,是否需要还继续链接:
[root@localhost_01 sbin]# > /root/.ssh/
authorized_keys         authorized_keys_xshell  id_rsa.pub              
authorized_keys.bakup   id_rsa                  known_hosts             
[root@localhost_01 sbin]# > /root/.ssh/known_hosts     #清空这个文件里面的信息:
[root@localhost_01 sbin]# ssh -p 56888 192.168.149.129      #再次登录时则会询问:
The authenticity of host '[192.168.149.129]:56888 ([192.168.149.129]:56888)' can't be established.
ECDSA key fingerprint is SHA256:mtOEV1y+2ErXFWqQRL0rYCQGGuVE7z4n1is+2dPQc7E.
ECDSA key fingerprint is MD5:d9:f7:03:de:c4:f5:40:89:be:3c:25:6d:70:6a:49:a5.
Are you sure you want to continue connecting (yes/no)? yes

[root@localhost_01 sbin]# ./1.expect
-bash: ./1.expect: 权限不够
[root@localhost_01 sbin]# chmod a+x 1.expect 
[root@localhost_01 sbin]# ./1.expect 
spawn ssh -p 56888 root@192.168.149.129
The authenticity of host '[192.168.149.129]:56888 ([192.168.149.129]:56888)' can't be established.
ECDSA key fingerprint is SHA256:mtOEV1y+2ErXFWqQRL0rYCQGGuVE7z4n1is+2dPQc7E.
ECDSA key fingerprint is MD5:d9:f7:03:de:c4:f5:40:89:be:3c:25:6d:70:6a:49:a5.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.149.129]:56888' (ECDSA) to the list of known hosts.
root@192.168.149.129's password: 
Last login: Thu Oct  4 00:33:35 2018 from 192.168.149.135
[root@localhost_02 ~]#

expect远程执行命令:

远程执行完命令,并退出:

[root@localhost_01 sbin]# vim 2.expect  
#!/usr/bin/expect
set user "root"
set passwd "nihao123!"
set port "-p 56888"
spawn ssh $port  $user@192.168.180.135

expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "$passwd\r" }
}
expect "]*"
send "touch /tmp/12.txt\r"
expect "]*"
send "echo 1212 > /tmp/12.txt\r"
expect "]*"
send "exit\r"

注释:expect “]*” 星号表示通配,匹配右边的所有字符(不管是root用户普通用户):
注释:send 表示执行这个命令:

3:需要加执行权限并执行这个命令:

[root@localhost_01 sbin]# chmod a+x 2.expect 
[root@localhost_01 sbin]# ll 2.expect 
-rwxr-xr-x 1 root root 307 104 16:09 2.expect
[root@localhost_01 sbin]# ./2.expect 
spawn ssh -p 56888 root@192.168.149.129
root@192.168.149.129's password: 
Last login: Thu Oct  4 06:00:22 2018 from 192.168.149.130
[root@localhost_02 ~]# touch /tmp/12.txt
[root@localhost_02 ~]# echo 1212 > /tmp/12.txt

然后在129这台机器上查看是否创建成功:

[root@localhost_02 ~]# ls /tmp/12.txt 
/tmp/12.txt
[root@localhost_02 ~]# cat  /tmp/12.txt 
1212

4、expect脚本传递参数:
[root@localhost_01 sbin]# vim 3.expect

[root@localhost_01 sbin]# cat 3.expect 
#!/usr/bin/expect
set port [lindex $argv 0]   
#第一个参数,如果端口还是默认的22,则不需要定义次参数,依次往下定义即可:
set user [lindex $argv 1] 
#第二个参数
set host [lindex $argv 2]   
#第三个参数
set passwd "nihao123!"
set cm [lindex $argv 3]   
#第四参数
spawn ssh -p $port $user@$host
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
}
expect "]*"
send "$cm\r"
expect "]*"
send "exit\r"

1:增加执行权限: chmod +x 3.expect

[root@localhost_01 sbin]# chmod a+x 3.expect 
[root@localhost_01 sbin]# ll 3.expect 
-rwxr-xr-x 1 root root 369 104 16:37 3.expect

2:执行此脚本:

./3.expect 第一个参数(port) 第二个参数(user) 第三个参数(host) 第四个参数(command)

[root@localhost_01 sbin]# ./3.expect 56888 root 192.168.149.129 ls
spawn ssh -p 56888 root@192.168.149.129
root@192.168.149.129's password: 
Last login: Thu Oct  4 06:24:37 2018 from 192.168.149.130
[root@localhost_02 ~]# ls
1  anaconda-ks.cfg  bash  CentOS7-Base-163.repo  link  shell  test  test.txt

同样,也支持多条参数: 多条命令最后用双引号括住,然后用分号分割则可以:

[root@localhost_01 sbin]# ./3.expect 56888 root 192.168.149.129 "ls;w"
spawn ssh -p 56888 root@192.168.149.129
root@192.168.149.129's password: 
Last login: Thu Oct  4 06:32:46 2018 from 192.168.149.130
[root@localhost_02 ~]# ls;w
1  anaconda-ks.cfg  bash  CentOS7-Base-163.repo  link  shell  test  test.txt
 06:32:57 up 19:42,  2 users,  load average: 0.42, 0.23, 0.16
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    192.168.149.130  06:32    1.00s  0.02s  0.01s w
root     pts/1    192.168.149.135  00:33   26:57   0.03s  0.03s -bash

注释:如果是执行多条命令的话,也可以传递多个参数,如果要是把多个命令当成一个参数来传递的话,则需要用双引号引起来,并且用分号分割才可以:
同样:当我们传递第四个参数时: vmstat模式超时时间是10秒:

[root@localhost_01 sbin]# ./3.expect 56888 root 192.168.149.129 "w;vmstat 1"
spawn ssh -p 56888 root@192.168.149.129
root@192.168.149.129's password: 
Last failed login: Thu Oct  4 06:56:10 CST 2018 from 192.168.149.130 on ssh:notty
There were 2 failed login attempts since the last successful login.
Last login: Thu Oct  4 06:37:26 2018 from 192.168.149.130

注释:也可以设置永不超时: 需要在执行命令(也就是第四个参数)后面添加 set timeout -1
expect “]*”

send "$cm\r"
#set timeout -1             #表示永不超时:
#set timeout  5             #也可以手动指定具体的秒数:  本次5ms:  
expect "]*"

send “exit\r”

1、 expect同步文件: 自动同步文件; 一台机器向一台机器同步文件:
1:在一台机器上,把文件同步到其他机器上;并添加执行权限:

[root@localhost_01 sbin]# cat 4.expect 
#!/usr/bin/expect
set passwd "nihao123!"
spawn rsync -av -e "ssh -p 56888" root@192.168.149.129:/tmp/12.txt /tmp/
expect {
"yes/no" { send "yes\r"}
"password:" { send $passwd\r}
}
expect eof
[root@localhost_01 sbin]# chmod a+x 4.expect 

2:执行这个脚本: ./4.expect 并查看同步后的文件内容:

[root@localhost_01 sbin]# ./4.expect 
spawn rsync -av -e ssh -p 56888 root@192.168.149.129:/tmp/12.txt /tmp/
root@192.168.149.129's password: 
receiving incremental file list
12.txt

sent 43 bytes  received 96 bytes  278.00 bytes/sec
total size is 5  speedup is 0.04
[root@localhost_01 sbin]# ls /tmp/12.txt 
/tmp/12.txt
[root@localhost_01 sbin]# ll /tmp/12.txt 
-rw-r--r-- 1 root root 5 Oct  4 06:04 /tmp/12.txt

注释:expect eof表示只有spawn执行命令的结果才会被expect捕捉到,因为spawn会启动一个进程,只有这个进程相关信息才会被捕捉到,注意包括标准输入的提示信息,eof 和timeout:

如下测试,注释掉expect eof,则还没来得及传输了,就退出来了(仅仅到输入密码的那里)

[root@localhost_01 sbin]# vim 4.expect 
#!/usr/bin/expect
set passwd "nihao123!"
spawn rsync -av -e "ssh -p 56888" root@192.168.149.129:/tmp/12.txt /tmp/
expect {
"yes/no" { send "yes\r"}
"password:" { send $passwd\r}
}
#expect eof      #注释掉:
[root@localhost_01 sbin]# rm -fr /tmp/12.txt 
[root@localhost_01 sbin]# ./4.expect 
spawn rsync -av -e ssh -p 56888 root@192.168.149.129:/tmp/12.txt /tmp/
root@192.168.149.129's password: [root@localhost_01 sbin]#

2、expect指定host和要同步的文件:

[root@localhost_01 sbin]# cat 5.expect 
#!/usr/bin/expect
set passwd "nihao123!"
set host [lindex $argv 0]
set file [lindex $argv 1]
spawn rsync -av -e "ssh -p 56888" $file root@$host:$file
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r"}
}
expect eof
[root@localhost_01 sbin]# chmod a+x 5.expect 

注释:用变量定义的文件所在目录要写绝对路径才可:
如:同步本机目录/tmp/12.txt到远端主机192.168.149.129的/tmp/目录下:

[root@localhost_01 sbin]# ./5.expect 192.168.149.129 "/tmp/12.txt"
spawn rsync -av -e ssh -p 56888 /tmp/12.txt root@192.168.149.129:/tmp/12.txt
root@192.168.149.129's password: 
sending incremental file list
12.txt

sent 100 bytes  received 35 bytes  90.00 bytes/sec
total size is 9  speedup is 0.07

注释:因为同步rsync需要借助ssh的端口,如果是默认的22端口或者自定义的,那如果有多台机器的ssh的端口各不相同,那是否可以把port当成一个参数来传递:如下:

[root@localhost_01 sbin]# vim 5.expect 
#!/usr/bin/expect
set passwd "nihao123!"
set port [lindex $argv 0]
set host [lindex $argv 1]
set file [lindex $argv 2]
spawn rsync -av -e  "ssh -p $port" $file root@$host:$file
expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r"}
}
expect eof
[root@localhost_01 sbin]# ./5.expect 56888 192.168.149.129 "/tmp/12.txt"
spawn rsync -av -e ssh -p 56888 /tmp/12.txt root@192.168.149.129:/tmp/12.txt
root@192.168.149.129's password: 
sending incremental file list

sent 44 bytes  received 12 bytes  37.33 bytes/sec
total size is 9  speedup is 0.16

版权声明:本文为weixin_45507491原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。