Expect 自动交互式程序

Posted alinuxer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Expect 自动交互式程序相关的知识,希望对你有一定的参考价值。

1. Expect 自动交互式程序介绍及安装

1.1 Expect 介绍

        Expect 是一个用来实现自动化交互功能的软件套件。

1.2 为什么使用 Expect

        在现今的企业运维中,自动化运维已经成为运维的主流趋势,但是在很多情况下,执行系统命令或程序时,系统会以交互式的形式要求运维人员输入指定的字符串,之后才能继续执行命令。例如:

① 为用户设置密码时,一般情况下就需要手工输入两次密码

② 使用 SSH 远程连接服务时,第一次连接要和系统实现两次交互式输入:

        简单地说,Expect 就是用来自动实现与交互式程序通信的,而无需管理员手工干预。比如 SSH 、FTP 远程连接等,正常情况下都需要手工与它们进行交互,而使用 Expect 就可以模拟手工交互的过程,实现自动与远端程序的交互,从而达到自动化运维的目的。

1.3 Expect 的自动交互工作流程简单说明

        spawn 启动指定进程 --->expect 获取期待的关键字 ---> send 向指定进程发送指定字符 ---> 执行完退出

1.4 安装 Expect 软件

        在管理机上,确保机器可以正常上网,并设置好 yum 安装源,然后执行 yum install expect -y 安装。

        安装过程如下:

[[email protected] ~]# rpm -qa expect      #  检查是否安装。(没有输出说明未安装)

[[email protected] ~]# yum install expect -y    # 执行安装命令。

[[email protected] ~]# rpm -qa expect   #  再次检查是否安装(安装成功会输出版本号)。

expect-5.44.1.15-5.el6_4.x86_64  

[[email protected] ~]# which expect   #  查看命令路径。

/usr/bin/expect

2.Expect 自动化交互式程序应用实践

2.1 小试牛刀:通过 Expect 自动交互功能查看远程服务器负载

准备两台服务器,IP 和主机名列表为:

IP 地址

主机名

192.168.136.142

nfs-server

192.168.136.141

lamp01

 

 

 

 

 

 

 

远程连接查看负载

[[email protected] ~]# ssh -p22 [email protected] uptime

[email protected]‘s password:  #  此处提示需要手工输入密码。

22:28:32 up  1:51,  1 user,  load average: 0.00, 0.00, 0.00

利用 expect 的功能实现自动交互的脚本

[[email protected] ~]# cat ex.exp

#!/usr/bin/expect    #  脚本开头解释器。

spawn ssh [email protected] uptime

expect "*password"

send "111111 "

expect eof    #  处理完毕后结束 expect 。

[[email protected] ~]# expect ex.exp

spawn ssh [email protected] uptime

[email protected]‘s password:

 22:34:38 up  1:57,  1 user,  load average: 0.00, 0.00, 0.00

提示:此时不需输入密码就自动连接到远端机器执行 ssh 命令了。

2.2 Expect 程序自动交互的重要命令及示例

2.2.1 spawn 命令

        在 Expect 自动交互程序执行的过程中,spawn 命令是一开始就需要使用的命令,通过 spawn 执行一个命令或程序,之后所有的 Expect 操作都会在这个执行过的命令或程序进程中进行,包括自动交互功能,因此如果没有 spawn 命令,Expect 程序将无法实现自动交互。

        语法:spawn 【选项】  【需要自动交互的命令或程序】

        例如:spawn ssh [email protected] uptime    # (Expect 脚本中命令)

        在 spawn 命令的后面,直接加上执行的命令或程序(例如 ssh 命令)等,除此之外,spawn 还支持如下选项:(不常用,了解)

        -open  表示启动文件进程。

        -ignore 表示忽略某些信号。

        提示:使用 spawn 命令是程序实现自动化交互工作流程中的第一步,也是最关键的一步。   

2.2.2 expect 命令

        在 Expect 自动交互程序执行的过程中,当使用 spawn 命令执行一个命令或程序之后,会显示出某些交互式信息,expect 命令的作用就是获取 spawn 命令执行后的信息,看看是否和其事先制定的相匹配,一旦匹配上指定的内容就执行 expect 后面的动作,expect 命令也有一些选项,用的最多的一个就是:-re 表示使用正则表达式 的方式来匹配。

        语法:expect 【表达式】  【动作】

        例如:spawn ssh [email protected] uptime

                  expect "*password" {send "123456 "}   # (Expect 脚本中命令) 。

范例 1:执行 ssh 命令远程获取服务器负载值,并要求实现自动输入密码。

方法一:将 expect 和send 放在同一行:

[[email protected] ~]# cat ex01.exp

#!/usr/bin/expect    #  脚本开头解释器。

spawn ssh [email protected] uptime

expect "*password"   {send "111111 "}

expect eof    #  处理完毕后结束 expect 。

方法二:将 expect 和send 放在不同行:

[[email protected] ~]# cat ex02.exp

#!/usr/bin/expect    #  脚本开头解释器。

spawn ssh [email protected] uptime

expect "*password"

send "111111 "

expect eof    #  处理完毕后结束 expect 。

上面两种方法的执行结果是一样的:

[[email protected] ~]# expect ex.exp

spawn ssh [email protected] uptime

[email protected]‘s password:

 22:34:38 up  1:57,  1 user,  load average: 0.00, 0.00, 0.00

提示:expect 命令还可以在一个 expect 匹配中多次匹配不同的字符串,并给出不同的处理动作。只需要将匹配的所有字符串放在一个 { } 即可(需要借助 exp_continue 实现继续匹配)。

范例二:执行 ssh 命令远程获取服务器负载值,并要求实现自动输入 yes 及用户密码。

实现的脚本:

[[email protected] ~]# cat ex03.exp

#!/usr/bin/expect

spawn ssh [email protected] uptime

expect  {   #  大括号前要有空格。

      "yes/no"     {exp_send "yes ";exp_continue}

      "*password"  {exp_send "111111 "}

}

expect eof   #  处理完毕后结束 expect 。

执行结果:

① 首先清除密钥文件使其出现 yes/no 提示信息:

[[email protected] ~]# ll ~/.ssh/known_hosts

-rw-r--r-- 1 root root 1191 Dec 11 01:58 /root/.ssh/known_hosts

[[email protected] ~]# rm -f ~/.ssh/known_hosts

② 执行脚本:

[[email protected] ~]# expect ex03.exp

spawn ssh [email protected] uptime

The authenticity of host ‘192.168.136.141 (192.168.136.141)‘ can‘t be established.

RSA key fingerprint is 8a:f2:af:0c:86:03:5c:35:25:75:5d:75:1d:9f:3c:8b.

Are you sure you want to continue connecting (yes/no)? yes    #  expect 自动输入 yes 。

Warning: Permanently added ‘192.168.136.141‘ (RSA) to the list of known hosts.

[email protected]‘s password:     #  expect 自动输入密码。

 23:06:05 up  2:29,  1 user,  load average: 0.00, 0.00, 0.00

范例三:利用 expect 响应 Shell 脚本中的多个 read 读入:

① 准备数据:利用 read 提示用户输入,创造交互式输入。

[[email protected] ~]# cat expread.sh

#!/bin/sh

read -p ‘please input your username:‘ name

read -p ‘please input your password:‘ pass

read -p ‘please input your email:‘ mail

echo -n "your name is $name,"

echo -n "your password is $pass,"

echo -n "your email is $mail."

② 执行结果:(下面的提示输入只能手动输入)

[[email protected] ~]# sh  expread.sh

please input your username:alinuxer

please input your password:111111

please input your email:[email protected]

your name is alinuxer,your password is 111111,your email is [email protected]

问题:开发 expect 自动化脚本,根据需求自动输入多个字符串:

[[email protected] ~]# cat ex04.exp

#!/usr/bin/expect

spawn /bin/sh expread.sh

expect {

    "username" {exp_send "alinuxer ";exp_continue}

    "*pass" {exp_send "123456 ";exp_continue}

    "*mail" {exp_send "[email protected] "}

}

expect eof    #  处理完毕后结束 expect 。

执行结果如下:  (回车后无需任何人工交互,直接输出结果)              

[[email protected] ~]# expect ex04.exp

spawn /bin/sh expread.sh

please input your username:alinuxer

please input your password:123456

please input your email:[email protected]

your name is alinuxer,your password is 123456,your email is [email protected]

2.2.3 send 命令

        在上面的案例中用到的 exp_send 和 send 命令是 Expect 中的动作命令,用法类似,即在 expect 命令匹配指定的字符串后,发送的指定的字符串给系统,这两个命令支持一些特殊转义符号如 (回车)、 (换行)、 (制表符)等

        参数:

        - i   指定 spawn_id ,用来向不同的 spawn_id 进程发送命令,是进行多程序控制的参数。

        - s  s 代表 slowly ,即控制发送的速度使用的时候要与 expect 中的变量 send_slow 相关联。

2.2.4 exp_continue 命令

        一般处于 expect 命令中,属于一种动作命令,一般使用在匹配多次字符串的动作中,即让 Expect 程序继续匹配的意思。

        提示:如果需要一次匹配多个字符串,那么不同的匹配之间就要加上 exp_continue ,否则 expect 将不会自动输入指定的字符串。最后一个结尾就不需要加 exp_continue 了,因为已经匹配完成了。

2.2.5 send_user 命令

        该命令可以用来打印 Expect 脚本信息,类似 Shell 里的 echo (-e 参数)命令,而默认的 send、exp_send 命令都是将字符串输出到 Expect 程序中去。

示例如下:

[[email protected] ~]# vi ex05.exp

#!/usr/bin/expect

send_user "I am alinuxer. "

send_user "I like playing basketball. "

send_user "My girlfriend is KeXin. "

执行结果如下:

[[email protected] ~]# expect ex05.exp

I am alinuxer.

I like playing basketball.

My girlfriend is KeXin.

2.2.6 exit 命令

        该命令的功能类似于 Shell 中的 exit ,即直接退出 Expect 脚本,除了最基本的退出脚本功能外,还可以利用该命令对脚本做一些关闭前的清理和提示等工作。

示例如下:

[[email protected] ~]# cat ex06.exp

#!/usr/bin/expect

send_user "I am alinuxer. "

send_user "I like playing basketball. "

send_user "My girlfriend is KeXin. "

exit -onexit {send_user "I am so cool! "

}

执行结果如下:

[[email protected] ~]# expect ex06.exp

I am alinuxer.

I like playing basketball.

My girlfriend is KeXin.

I am so cool!

Expect 常用命令总结:见<< 老男孩 Shell 编程实战 P325 >> 【略】

3. Expect 程序普通变量

定义变量的基本语法: set  变量名   变量值

打印变量的基本语法: puts $变量名

3.1 定义变量及输出变量示例

[[email protected] ~]# cat ex07.exp

#!/usr/bin/expect

set password "123456"

puts $password

send_user "$password "    #  send_user 也可以打印输出变量。

执行结果如下:

[[email protected] ~]# expect ex07.exp

123456

123456

3.2 Expect 程序特殊参数变量

        在 Expect 里也有与 Shell 脚本里的$0、$1、$# 等类似的特殊参数变量,用于接收及控制 Expect 脚本参数。   

        在 Expect 中 $argv 表示参数数组,可以使用 [lindex $argv n] 接收 Expect 脚本传参,n 从零开始,分别表示第一个参数([lindex $argv 0]),第二个参数([lindex $argv 1]),第三个参数([lindex $argv 2])......

范例 1:定义及输出特殊参数变量:

[[email protected] ~]# cat ex08.exp

#!/usr/bin/expect

#define var

set file [lindex $argv 0]    #  定义特殊参数变量。

set host [lindex $argv 1]    #  定义特殊参数变量。

set dir [lindex $argv 2]    #  定义特殊参数变量。

send_user "$file $host $dir "       #  打印特殊参数变量。

puts "$file $host $dir "    #  打印特殊参数变量。

执行结果如下:(命令行传参)

[[email protected] ~]# expect ex08.exp a.log 192.168.136.142 /tmp

a.log   192.168.136.142 /tmp

a.log   192.168.136.142 /tmp

        提示:注意 Expect 接受参数的方式和 bash 脚本的方式的区别。除了基本的位置参数外,Expect 也支持其他的特殊参数,例如:$argc 表示传参的个数,$argv0 表示脚本的名字。

范例 2:针对 Expect 脚本传参的个数及脚本名参数的实践:

脚本如下:

[[email protected] ~]# cat ex09.exp

#!/usr/bin/expect

#define var

set file [lindex $argv 0]

set host [lindex $argv 1]

set dir [lindex $argv 2]

send_user "$file $host $dir "

puts "$file $host $dir "

puts $argc

puts $argv0

执行的结果:

[[email protected] ~]# expect ex09.exp b.log 192.168.136.141 /opt

b.log   192.168.136.141 /opt

b.log   192.168.136.141 /opt

3                #  打印参数个数($argc)。

ex09.exp   #  输出脚本名字($argv0)。

3.3 Expect 程序中的 if 条件句

范例 1 :使用 if 语句判断脚本传参的个数,如果不符合则给予提示:

脚本如下:

[[email protected] ~]# cat ex10.exp

#!/usr/bin/expect

if { $argc != 3 } {

    send_user "Usage:expect $argv0 file host dir "

    exit

}

#define var

set file [lindex $argv 0]

set host [lindex $argv 1]

set dir [lindex $argv 2]

puts "$file $host $dir"

执行的结果:

[[email protected] ~]# expect ex10.exp c.log 192.168.136.141 /home

c.log   192.168.136.141 /home

范例 2 :使用 if 语句判断脚本传参的个数,无论是否符合都给予提示:

脚本如下:

[[email protected] ~]# cat ex11.exp

#!/usr/bin/expect

if { $argc != 26 } {

     puts "bad"

} else {

     puts "good"

}

执行结果如下:

[[email protected] ~]# expect ex11.exp {a..z}

good

[[email protected] ~]# expect ex11.exp {a..y}

bad

3.4 Expect 中的关键字

        Expect 中的特殊关键字用于匹配过程,代表某些特殊的含义或状态,一般只用于 Expect 命令中而不能在 Expect 命令外面单独使用。

        ① eof   end-of-file  用于匹配结束符。

        ②  timeout  控制时间的一个关键字变量。可通过为该变量赋值来规定整个 Expect 操作的时间。

4. 企业生产场景下的 Expect 案例

4.0 环境准备:准备三台服务器,IP 和主机名见下表

IP 地址

主机名

角色

192.168.136.142

nfs-server

管理机

192.168.136.141

lamp01

被管理机 1

192.168.136.143

lnmp02

被管理机 2

 

 

 

 

 

 

 

 

 

4.1 开发 Expect 脚本实现自动交互式批量执行命令

1)实现 Expect 自动交互的脚本:         

[root[email protected] ~]# cat zidongjiaohu.exp

#!/usr/bin/expect

if { $argc != 2 } {

     puts "Usage:expect $argv0 ip command"

     exit

}

#define var

set ip  [lindex $argv 0]

set cmd [lindex $argv 1]

set password "111111"

#

spawn ssh [email protected]$ip $cmd

expect {

    "yes/no"     {send "yes ";exp_continue}

    "*password"  {send "$password "}

}

expect eof        

执行结果如下:

[[email protected] ~]# expect zidongjiaohu.exp 192.168.136.143 uptime

spawn ssh [email protected] uptime

[email protected]‘s password:

 23:57:06 up  3:43,  1 user,  load average: 0.00, 0.00, 0.00

[[email protected] ~]# expect zidongjiaohu.exp 192.168.136.141 "free  -m"

spawn ssh [email protected] free  -m

[email protected]‘s password:

             total       used       free     shared    buffers     cached

Mem:           980        177        802          0         26         45

-/+ buffers/cache:        105        874

Swap:         1023          0       1023

2)利用 Shell 循环执行 Expect 脚本命令:

[[email protected] ~]# cat sh_piliang_exp.sh

#!/bin/sh

if [ $# != 1 ]

   then

     echo $"Usage:$0 cmd"

     exit 1

fi

cmd=$1

for n in 130 141 143

do

   expect zidongjiaohu.exp 192.168.136.$n  "$cmd"

done

执行结果如下:

[[email protected] ~]# sh sh_piliang_exp.sh uptime

spawn ssh [email protected] uptime

The authenticity of host ‘192.168.136.130 (192.168.136.130)‘ can‘t be established.

RSA key fingerprint is 8a:f2:af:0c:86:03:5c:35:25:75:5d:75:1d:9f:3c:8b.

Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added ‘192.168.136.130‘ (RSA) to the list of known hosts.

[email protected]‘s password:

 21:35:26 up  3:52,  1 user,  load average: 0.00, 0.00, 0.00

spawn ssh [email protected] uptime

[email protected]‘s password:

 22:40:24 up  3:52,  1 user,  load average: 0.00, 0.00, 0.00

spawn ssh [email protected] uptime

[email protected]‘s password:

 00:05:41 up  3:52,  1 user,  load average: 0.00, 0.00, 0.00

4.2 开发 Expect 脚本实现自动交互式批量发送文件或目录  

1)实现 Expect 自动化交互的脚本:

[[email protected] ~]# cat zidongjiaohu01.exp

#!/usr/bin/expect

if { $argc != 3 } {

     puts "Usage:expect $argv0 file host dir"

     exit

}

 

#define var

set file [lindex $argv 0]

set host [lindex $argv 1]

set dir  [lindex $argv 2]

set password "111111"

spawn scp -p22 -rp $file [email protected]$host:$dir

expect {

    "yes/no"  {send "yes ";exp_continue}

    "*password" {send "$password "}

}

expect eof

执行结果如下:

[[email protected] ~]# expect zidongjiaohu01.exp

Usage:expect zidongjiaohu01.exp file host dir

[[email protected] ~]# expect zidongjiaohu01.exp /etc/hosts 192.168.136.130:/tmp

Usage:expect zidongjiaohu01.exp file host dir

[[email protected] ~]# expect zidongjiaohu01.exp /etc/hosts 192.168.136.130 /tmp

spawn scp -p22 -rp /etc/hosts [email protected]:/tmp

[email protected]‘s password:

hosts                                               100%  255     0.3KB/s   00:00

在 192.168.136.130 查看发送 hosts 文件结果是否成功:

[[email protected] tmp]# ll   #  执行前没有 hosts 文件。        

total 4   

drwxr-xr-x  2 root root 4096 Dec 10 21:46 data

-rw-------. 1 root root    0 Dec  9 22:11 yum.log

[[email protected] tmp]# ll   #  执行后有了 hosts 文件。

drwxr-xr-x  2 root root 4096 Dec 10 21:46 data

-rw-r--r--  1 root root  255 Dec 11 01:53 hosts

-rw-------. 1 root root    0 Dec  9 22:11 yum.log

2)利用 Shell 循环执行 Expect 脚本命令:

[[email protected] ~]# cat sh_piliang_exp01.sh

#!/bin/sh

if [ $# -ne 2 ]

   then

     echo $"Usage:$0 file dir"

     exit 1

fi

file=$1

dir=$2

for n in 130 141 143

do

     expect zidongjiaohu01.exp $file 192.168.136.$n $dir

done

执行结果如下:表示批量发送文件成功。

[[email protected] ~]# sh sh_piliang_exp01.sh /etc/hosts /tmp

spawn scp -p22 -rp /etc/hosts [email protected]:/tmp

[email protected]‘s password:

hosts           100%  255     0.3KB/s   00:00    

spawn scp -p22 -rp /etc/hosts [email protected]:/tmp

[email protected]‘s password:

hosts           100%  255     0.3KB/s   00:00    

spawn scp -p22 -rp /etc/hosts [email protected]:/tmp

[email protected]‘s password:

hosts          100%  255     0.3KB/s   00:00

4.3 开发 Expect 脚本实现自动交互式批量执行 Shell 脚本   

见<<老男孩 Shell 高级编程实战 P334~336>>  【略】

4.4 开发 Expect 脚本实现自动交互式批量分发公钥   

1)本地生成密钥对:【一路回车】

[[email protected] ~]# ssh-keygen -t dsa

Generating public/private dsa key pair.

Enter file in which to save the key (/root/.ssh/id_dsa):

/root/.ssh/id_dsa already exists.

Overwrite (y/n)?

[[email protected] ~]# ll .ssh/

total 8

-rw------- 1 root root 668 Dec 11 01:55 id_dsa

-rw-r--r-- 1 root root 605 Dec 11 01:55 id_dsa.pub

2)开发 Expect 脚本自动化交互分发公钥到所有的服务器:

[[email protected] ~]# cat fenfa_pub.exp

#!/usr/bin/expect

#created by ZhangLei

if { $argc != 2 } {

     send_user "Usage:expect $argv0 file host "

     exit

}

#define var

set file [lindex $argv 0]

set host [lindex $argv 1]

set password "111111"

#start exec command

spawn ssh-copy-id -i $file "-p 22 [email protected]$host"

expect {

    "yes/no"  {send "yes ";exp_continue}

    "*password" {send "$password "}

}

expect eof    

3)开发 Shell 脚本循环执行 Expect 脚本:

[[email protected] ~]# cat sh_xunhuanfenfa_pub.sh

#!/bin/sh

for n in 130 141 143

do

   expect fenfa_pub.exp ~/.ssh/id_dsa.pub 192.168.136.$n

done

执行 Shell 脚本即可分发公钥到所有服务器,略。


以上是关于Expect 自动交互式程序的主要内容,如果未能解决你的问题,请参考以下文章

expect自动化交互式操作

自动化交互程序Expect应用实践

Expect交互自动化

expect知识梳理

Here Document 免交互与 Expect 自动化交互

Linux Shell交互式自动化运维程序