**
EXPECT全解
**
Expect是Unix系统中用来进行自动化控制和测试的软件工具,由DonLibes制作,作为Tcl脚本语言的一个扩展,应用在交互式软件中如telnet,ftp,Passwd,fsck,rlogin,tip,ssh等等。该工具利用Unix伪终端包装其子进程,允许任意程序通过终端接入进行自动化控制;也可利用Tk工具,将交互程序包装在X11的图形用户界面中。
简单地说,expect是一个工具,可以根据用户设定的规则和系统进程进行自动化交互,例如远程登陆的密码输入、自动化的执行远程命令。
1. 介绍
expect是建立在tcl(参见:Tcl/Tk快速入门 )基础上的一个工具,它可以让一些需要交互的任务自动化地完成。相当于模拟了用户和命令行的交互操作。
一个具体的场景:远程登陆服务器,并执行命令
登录时输入密码需要交互,bash脚本无法完成,可以使用expect来完成。
2. 安装
下面介绍两种安装方式
yum 安装
yum install -y expect
源码编译安装
expect 依赖于 tcl, 所以需要首先安装 tcl。可以使用rpm检查是否已经安装tcl:
rpm -qa | grep tcl
如果已安装,则会打印出tcl软件信息
安装过程参考:linux expect 安装 http://www.cnblogs.com/daojian/archive/2012/10/10/2718390.html
tcl 地址:https://sourceforge.net/projects/tcl/files/Tcl/ 选择一个版本
expect 地址:https://sourceforge.net/projects/expect/files/Expect/ 选择一个版本
注意:wget 下载的时候需要加上 –no-check-certificate, 不检查网站证书
3. 原理与工作机制
首先使用 spawn 开启一个会话,然后使用 expect-send 对来执行交互式操作。
spawn 后面跟上一个命令操作,表示开启一个会话。expect 等待输出特定的字符串(通常是提示符),然后使用send 发送交互字符串。比如:
spawn ssh username@host # 远程登录
expect "*assword" # 提示为:"username@host's password:", 等待用户输入密码
send "${password}\r" # 这时使用send模拟用户输入密码的字符串,完成登录验证
4. 基本语法介绍
脚本解释器
脚本中首先引入文件,表明使用的是哪一个shell
#!/usr/bin/expect
set
设置会话超时时间为30s, 若不限制超时时间则应设置为-1
set timeout 30
set 还可以设置变量
# 使用变量语句:$param 或者 ${param}({}用来避免param和后面的字符串拼接起来导致错误)
set param "param_str"
set param 1
spawn
spawn 后面跟一个命令,开启一个会话
spawn ${cmd} # for example : spawn su root
expect - send
expect 接收命令执行后的输出,然后和期望字符串匹配,若对应这执行相应的send来发送交互信息。
expect "$case1" {send "$respond1\r"} # 这一行等同于下面两行
expect "$case1"
send "$response1\r"
expect 可以有多个分支,就像switch语句一样。
expect
{
"$case1" {send "$response1\r"}
"$case2" {send "$response2\r"}
"$case3" {send "$response3\r"}
}
结束符
expect eof :等待执行结束,若没有这一句,可能导致命令还没执行,脚本就结束了
interact : 执行完成后保持交互状态, 这时可以手动输入信息
注:expect eof 与 interact 二选一即可
接收参数
参数存在argv中,使用第一个参数如下:
set param0 [lindex $argv 0]
1
2
$argc表示参数个数,判断语句如下:
if {$argc < 1} {
#do something
send_user "usage: $argv0 <param1> <param2> ... "
exit
注:$argv0 是脚本名,但[lindex $argv 0]是第一个参数 param1, [lindex $argv 1]是第二个参数 param2, 以此类推
send_user 用来显示信息到父进程(一般为用户的shell)的标准输出。
**
expect_scp
==========
**
一.解决scp交互
# vim expect_scp 解决交互执行的文件
---------------------------------------------------------------------------------------------
#!/usr/bin/expect
set timeout 10
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set src_file [lindex $argv 3]
set dest_file [lindex $argv 4]
spawn scp $src_file $username@$host:$dest_file
expect {
"(yes/no)?"
{
send "yes\n"
expect "*assword:" { send "$password\n"}
}
"*assword:"
{
send "$password\n"
}
}
expect "100%"
expect eof
---------------------------------------------------------------------------------------------
chmod +x expect_scp
二.调用与执行scp各文件
# vim scp.sh
----------------------------------------------------------------------------------------------
#!/bin/sh
list_file=$1
src_file=$2
dest_file=$3
cat $list_file | while read line
do
host_ip=`echo $line | awk '{print $1}'`
username=`echo $line | awk '{print $2}'`
password=`echo $line | awk '{print $3}'`
echo "$host_ip"
./expect_scp $host_ip $username $password $src_file $dest_file
done
---------------------------------------------------------------------------------------------
chmod +x scp.sh
三.远程信息文件
# vim passwd.txt
---------------------------------------------------------------------------------------------
192.168.0.100 root redhat
---------------------------------------------------------------------------------------------
四.总执行脚本
# vim all_scp.sh
-------------------------------------------------------------------------------------------------
bash /root/scp.sh /root/passwd.txt 本地源文件路径 目标文件路径
......................可以很多scp
-------------------------------------------------------------------------------------------------
chmod +x all_scp.sh
**
expect_ssh
==========
**
远程自动化执行脚本
1.实现远程执行动作脚本
# vim ssh.sh
#!/usr/bin/expect -f
set ipaddress [lindex $argv 0]
set passwd [lindex $argv 1]
set timeout 5
spawn ssh root@$ipaddress //这里主要是 @ 前的用户,是远程主机的用户,我这里全是 root
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$passwd\r" }
}
expect "*from*"
send "mkdir /tmp/wml\r" //这是远程执行命令,可以很多条
send "touch /tmp/wml/www\r"
#send "exit\r"
expect {
"?assword:" {
send "$PASSWORD\r"
exp_continue
}
}
2.调用执行动作与密码信息脚本,也是总执行脚本
# vim wml.sh
#!/bin/bash
for i in `awk '{print $1}' passwd.txt`
do
j=`awk -v I="$i" '{if(I==$1)print $2}' passwd.txt`
expect /root/ssh.sh $i $j #执行动作脚本
done
3.远程IP与密码文件
# vim passwd.txt
192.168.0.102 redhat
192.168.0.104 redhat
192.168.0.106 redhat
**
expect_scp_ssh
==============
**
【 expect批量自动 《分发文件》 & 《执行文件》 文件 】
注意:1. 所有脚本必须在同一目录下编辑执行,否则不会成功
2. 红色标记为主要改动的地方
一.分发文件
1.远程登陆脚本
# mkdir -p /root/auto/
# cd /root/auto/
# vim scp_login.sh // 此脚本一般情况下,不需要改动
---------------------------------------------------------------------
#!/usr/bin/expect
set timeout 10
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set src_file [lindex $argv 3]
set dest_file [lindex $argv 4]
spawn scp $src_file $username@$host:$dest_file
expect {
"(yes/no)?"
{
send "yes\n"
expect "*assword:" { send "$password\n"}
}
"*assword:"
{
send "$password\n"
}
}
expect "100%"
expect eof
------------------------------------------------------------------------
2. 分发调用脚本
# vim scp_executed.sh // 此脚本中,红色为上面登陆文件
---------------------------------------------------------------
#!/bin/sh
list_file=$1
src_file=$2
dest_file=$3
cat $list_file | while read line
do
host_ip=`echo $line | awk '{print $1}'`
username=`echo $line | awk '{print $2}'`
password=`echo $line | awk '{print $3}'`
echo "$host_ip"
/root/auto/scp_login.sh $host_ip $username $password $src_file $dest_file
done
-----------------------------------------------------------------------
3. 远程主机密码文件
# vim scp_passwd.sh // 此文件格式:IP 用户名 密码
-----------------------------------------------------------------------
192.168.0.100 root redhat
192.168.0.104 root redhat
192.168.0.106 root redhat
-----------------------------------------------------------------------
4.总执行scp脚本
# vim scp_all.sh // 此脚本格式:调用文件 密码文件 源文件目录 目标目录
----------------------------------------------------------------------
/root/auto/scp_executed.sh /root/auto/scp_passwd.sh /root/iptables.sh /root/
/root/auto/scp_executed.sh /root/auto/scp_passwd.sh 本地源文件路径 目标文件路径
----------------------------------------------------------------------
二.执行文件
1.远程登陆脚本
# vim ssh_login.sh
----------------------------------------------------------------------------------
#!/usr/bin/expect -f
set ipaddress [lindex $argv 0]
set passwd [lindex $argv 1]
set timeout 5
spawn ssh root@$ipaddress
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$passwd\r" }
}
expect "*from*"
send "/root/iptables.sh\r"
#send "exit\r"
expect {
"?assword:" {
send "$PASSWORD\r"
exp_continue
}
}
---------------------------------------------------------------------------------
// 上面脚本红色解析:set timeout 5 //在远程主机执行时,停顿的秒数,可以调整
root //本地主机的身份,可以是其它用户
/root/iptables.sh //远程主机上,执行的动作,可以更多
2. 远程主机密码文件
# vim ssh_passwd.sh // 此脚本格式:IP 用户名 密码
---------------------------------------------------------------------------------
192.168.0.100 redhat
192.168.0.104 redhat
192.168.0.106 redhat
---------------------------------------------------------------------------------
3.总执行ssh文件
# vim ssh_all.sh // 前两个红色标记为ssh的密码文件,后一个是ssh的登陆文件
--------------------------------------------------------------------------------
#!/bin/bash
for i in `awk '{print $1}' "/root/auto/ssh_passwd.sh"`
do
j=`awk -v I="$i" '{if(I==$1)print $2}' "/root/auto/ssh_passwd.sh"`
expect /root/auto/ssh_login.sh $i $j
done
--------------------------------------------------------------------------------
三.结合
# vim all_scp_ssh
--------------------------------------------------------------------------------
/root/auto/scp_all.sh
/root/auto/ssh_all.sh
--------------------------------------------------------------------------------
# chmod +x /root/auto -R
# /root/auto/all_scp_ssh