EazyDevelop社区 Expect 自动化运维工具使用指南

Expect 自动化运维工具使用指南

目的为了运维人员更好地管理批量机器,特此编写本文档。什么是 ExpectExpect 是基于 Tcl 的相对简单的一个免费的脚本编程工具语言,用来实现自动和交互式任务程序进行通信,无需人工干预。比如:SSH、SCP 等,这些程序都需要人工与其进行交互,Expect 就可以模拟人工交互过程,自动与远端程序进行交互,从而实现自...

青 nih  ·  2024-04-07 23:09:46 发布

目的

为了运维人员更好地管理批量机器,特此编写本文档。

什么是 Expect

Expect 是基于 Tcl 的相对简单的一个免费的脚本编程工具语言,用来实现自动和交互式任务程序进行通信,无需人工干预。比如:SSH、SCP 等,这些程序都需要人工与其进行交互,Expect 就可以模拟人工交互过程,自动与远端程序进行交互,从而实现自动化运维的目的。

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

虽然,使用 C、Python 等程序也可以实现自动交互,而 Expect 更专业、简单,跨平台支持(Linux 和 Windows),它就是为了系统管理和软件测试方面的自动交互类需求而产生的。

Expect 工作流程

Expect 的工作流程可以理解为:spawn 启动进程 -> expect 期待关键字 -> send 向进程发送字符 -> 退出结束。

安装 Expect

首先,配置好 yum 源,并能上网,然后执行:

yum install -y expect
  • 1.

安装后检查命令:

rpm -aq | grep expect
  • 1.

Expect 语法

Expect 中的命令是最重要的部分了,命令的使用语法如下:

命令 [选项] 参数
  • 1.
spawn

spawn 命令是 Expect 的初始命令,它用于启动进程,之后所有 Expect 操作都在这个进程中进行,如果没有 spawn 语句,整个 Expect 就无法执行了。spawn 使用方法如下:

spawn ssh root@10.172.10.10
  • 1.

spawn 命令后面,直接加上要启动的进程、命令等信息。除此之外,spawn 还支持其他选项如:

  • -open 启动文件进程,具体说明省略。
  • -ignore 忽略某些信号,具体说明省略。
expect

使用方法:

expect 表达式 动作 表达式 动作 ………………….
  • 1.

expect 命令用于等候一个相匹配内容的输出,一旦匹配上就执行 expect 后面的动作或命令,这个命令接受几个特有参数,用的最多的就是 -re,表示使用正则表达式的方式匹配,使用起来就像这样:

spawn ssh root@10.172.10.10
expect "password:" {send "123456\r"}
  • 1.
  • 2.

从上面的例子可以看出,expect 是依附与 spawn 命令的,当执行 ssh 命令后,expect 就匹配命令执行后的关键字 password:,如果匹配上到关键字就会执行后面包含在 {} 括号中的 sendexp_send 动作,匹配的动作可以放在二行,这样就不需要使用 {} 括号了,就像下面这样,实际完成的功能与上面是一样的:

spawn ssh root@10.172.10.10
expect "password:"
send "123456\r"
  • 1.
  • 2.
  • 3.

expect 命令还有一种用法,它可以在一个 expect 匹配中多次匹配关键字,并给出处理动作,中需要将关键字放在一个大括号中就可以了,当然后还要有 exp_continue

spawn ssh root@10.172.10.10
expect {
    "yes/no" {exp_send "yes\r";exp_continue}
    "*password:" {exp_send "123456\r"}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
exp_send 和 send

在上面的介绍中,我们已经看到了 exp_send 命令的使用,exp_send 命令是 Expect 中的动作命令,它还有一个完成同样工作的同胞:sendexp_send 命令可以发送一些特性符号,我们看到了 \r(回车),还有一些其他的比如:\n(换行)、\t(制表符)等等,这些都与 TCL 中的特殊符号相同。

spawn ssh root@10.172.10.10
expect {
    "yes/no" { send "yes\r"; exp_continue }
    "*password:" { exp_send "123456\r" }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

send 命令有几个可用的参数:

  • -i 指定 spawn_id,这个参数用来向不同 spawn_id 的进程发送命令,是进行多程序控制的关键参数。
  • -s s 代表 slowly,也就是控制发送的速度,这个参数使用的时候要与 Expect 中的变量 send_slow 相关联。
exp_continue

这个命令一般用在动作中,它被使用的条件比较苛刻,看下面的例子:

#!/usr/bin/expect
spawn ssh -p22 root@10.172.10.10 /sbin/ifconfig eth0
set timeout 60
expect {
    -timeout 1
    "yes/no" { exp_send "yes\r";exp_continue }
    "*password:" { exp_send "123456\r" }
    timeout { puts "expect was timeout by ivw."; return }
}
expect eof
exit
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

在这个例子中,可以发现 exp_continue 命令的使用方法,首先它要处于一个 expect 命令中,然后它属于一种动作命令,完成的工作就是从头开始遍历,也就是说如果没有这个命令,匹配第一个关键字以后就会继续匹配第二个关键字,但有了这个命令后,匹配第一个关键字以后,第二次匹配依然从第一个关键字开始。

send_user

send_user 命令用来把后面的参数输出到标准输出中去,默认的 sendexp_send 命令都是将参数输出到程序中去的,用起来就像这样:

send_user "please input passwd:"
  • 1.

这个语句就可以在标准输出中打印 please input passwd: 字符了。

#!/usr/bin/expect
If { $argc != 3 } {
send_user "usage:expect scp_expect.exp file host dir\n"
exit
#define var
set file [ lindex $argv 0 ]
set host [ lindex $argv 1 ]
set dir [ lindex $argv 2 ]
set password "123456"
#spawn
spawn scp -P22 $fle root@$host:$dir
expect {
    "yes/no" { send "yes\r";exp_continue }
    "*password:" { send "$password \r" }
}
expect eof
exit
#script usage
#expect scp_expect.exp file host dir
#./expect.exp /etc/hosts 10.172.10.10 /etc
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
exit

exit 命令功能很简单,就是直接退出脚本,但是可以利用这个命令对脚本做一些扫尾工作,比如下面这样:

exit -onexit {
     exec rm $tmpfile
     send_user "Good bye\n"
}
  • 1.
  • 2.
  • 3.
  • 4.
#!/usr/bin/expect
If { $argc != 3 } {
send_user "usage:expect scp_expect.exp file host dir\n"
exit
#define var
set file [ lindex $argv 0 ]
set host [ lindex $argv 1 ]
set dir [ lindex $argv 2 ]
set password "123456"
#spawn
spawn scp -P22 $fle root@$host:$dir
expect {
    "yes/no" { send "yes\r";exp_continue }
    "*password:" { send "$password \r" }
}
expect eof
exit -onexit {
     send_user "ivw say good bye to you!\n"
}
#script usage
#expect scp_expect.exp file host dir
#./expect.exp /etc/hosts 10.172.10.10 /etc
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
Expect 变量

Expect 中有很多有用的变量,它们的使用方法与 TCL 语言中的变量相同,比如:

set 变量名 变量值 #设置变量的方法

puts $变量名 #读取变量的方法

#define var
set file [ lindex $argv 0 ]
set host [ lindex $argv 1 ]
set dir [ lindex $argv 2 ]
set password "123456"
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
Expect 关键字

Expect 中的特殊关键字用于匹配过程,代表某些特殊含义或状态,一般用于 expect 族命令中而不能在外面单独使用,也可以理解为事件,使用上类似于:

expect eof { }
  • 1.
eof

eof(end-of-file)关键字用于匹配结束符,比如文件的结束符、FTP 传输停止等情况,在这个关键字后跟上动作来做进一步的控制,特别是 FTP 交互操作方面,它的作用很大。用一个例子来说明:

spawn ftp anonymous@10.172.10.10
expect {
   "password:" { exp_sed "who I'm I" }
   eof { ftp connect close }
}
Interact { }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
timeout

timeout 是 Expect 中的一个重要的变量,它是一个全局性的时间控制开关,可以通过为这个变量赋值来规定整个 Expect 操作的时间,注意这个变量是服务与 Expect 全局的,它不会纠缠于某个命令,即使命令没有任何错误,到时间仍然会激活这个变量,但这个时间到达以后除了激活一个开关之外不会做其他的事情,如何处理是脚本编写人员的事情,看看它的实际使用方法:

set timeout 60
spawn ssh root@10.172.10.10
expect "password:" { send "word\r" }
expect timeout { puts "Expect was timeout";return }
  • 1.
  • 2.
  • 3.
  • 4.

上面的处理中,首先将 timeout 变量设置为 60 秒,当出现问题的时候程序可能会停止下来,只要到 60 秒,就会激活下面的 timeout 动作,这里我打印一个信息并且停止了脚本的运行 - 你可以做更多其他的事情,看自己的意思。

在另一种 expect 格式中,我们还有一种设置 timeout 变量的方法,看下面的例子:

spawn ssh root@10.172.10.10
expect {
    -timeout 60
    -re "password:" {exp_send "word\r"}
    -re "TopsecOS#" { }
    Timeout { puts "Expect was timeout"; return }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

expect 命令中间加上一个小横杠,也可以设置 timeout 变量。

timeout 变量中,设置为 0 表示立即超时,-1 则表示永不超时。

expect {
   -timeout 20
   "yes/no" { send "yes\r";exp_continue }
   "*password:" { send "$password\r" }
   Timeout {puts "expect connect timeout,pls contact ivw."; return }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

生产场景 Expect 实战

自动 SCP 文件
vi autoscp.exp
#!/usr/bin/expect

set timeout 10
set user_hostname [lindex $argv 0]
set src_file [lindex $argv 1]
set dest_file [lindex $argv 2]
set password [lindex $argv 3]
spawn scp $src_file $user_hostname:$dest_file
    expect {
        "(yes/no)?"
        {
            send "yes\n"
            expect "*assword:" { send "$password\n"}
        }
        "*assword:"
        {
            send "$password\n"
        }
    }
expect "100%"
expect eof
#执行
expect autoscp.exp 10.8.17.87 /etc/hosts /tmp het@2018
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
vi scp_expect.exp
#!/usr/bin/expect
If { $argc != 3 } {
send_user "usage:expect scp_expect.exp file host dir\n"
exit
#define var
set file [ lindex $argv 0 ]
set host [ lindex $argv 1 ]
set dir [ lindex $argv 2 ]
set password "123456"
#spawn
spawn scp -P22 $fle root@$host:$dir
set timeout 60
expect {
    -timeout 20
    "yes/no" { send "yes\r";exp_continue }
    "*password:" { send "$password \r" }
   timeout { puts "expect connect timeout,pls contact ivw."; return }
}
expect eof
exit -onexit {
      send_user "ivw say good bye to you!"
}
#script usage
#expect scp_expect.exp file host dir
#./expect.exp /etc/hosts 10.172.10.10 /etc
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
Expect 批量分发
vi expect.exp
#!/usr/bin/expect
If { $argc != 3 } {
send_user "usage:expect scp_expect.exp file host dir\n"
exit
#define var
set file [ lindex $argv 0 ]
set host [ lindex $argv 1 ]
set dir [ lindex $argv 2 ]
set password "123456"
#spawn
spawn scp -P22 $fle root@$host:$dir
set timeout 60
expect {
    -timeout 20
    "yes/no" { send "yes\r";exp_continue }
    "*password:" { send "$password \r" }
   timeout { puts "expect connect timeout,pls contact ivw."; return }
}
expect eof
exit -onexit {
      send_user "ivw say good bye to you!"
}
#script usage
#expect scp_expect.exp file host dir
#./expect.exp /etc/hosts 10.172.10.10 /etc
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
Expect 批量分发 SSH 密钥
vi scp_expect_key.exp
#!/usr/bin/expect
If { $argc != 3 } {
send_user "usage:expect scp_expect_key.exp file host dir\n"
exit
#define var
set file [ lindex $argv 0 ]
set host [ lindex $argv 1 ]
set dir [ lindex $argv 2 ]
set password "123456"
#spawn
spawn scp -P22 -r -p $file root@$host:$dir
set timeout 60
expect {
    -timeout 20
    "yes/no" { send "yes\r";exp_continue }
    "*password:" { send "$password \r" }
   timeout { puts "expect connect timeout,pls contact ivw."; return }
}
expect eof
exit -onexit {
      send_user "ivw say good bye to you!"
}
#script usage
#expect scp_expect_key.exp file host dir
#./expect.exp /etc/hosts 10.172.10.10 /etc

vi fenfa_key.exp
#!/bin/sh
. /etc/init.d/functions
for ip in `cat iplist`
do
 expect scp_expect_key.exp /root/.ssh $ip /root >/dev/null 2>&1
 if [ $? -eq 0 ];then
   action "$ip" /bin/true
 else
   action "$ip" /bin/false
 fi
done
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
Expect 批量管理
vi ssh_expect.exp
#!/usr/bin/expect
If { $argc != 2 } {
send_user "usage:expect ssh_expect.exp file host dir\n"
exit
#define var
set ip [ lindex $argv 0 ]
set cmd [ lindex $argv 1 ]
set password "123456"
#spawn
spawn ssh -P22 root@$ip $cmd
set timeout 60
expect {
    -timeout 20
    "yes/no" { send "yes\r";exp_continue }
    "*password:" { send "$password \r" }
   timeout { puts "expect connect timeout,pls contact ivw."; return }
}
expect eof
exit
#script usage
#expect ssh_expect.exp file host dir
#./ssh_expect.exp 10.172.10.10 ifconfig 

vi free.exp
#!/bin/sh
. /etc/init.d/functions
for ip in `cat iplist`
do
 expect ssh_expect.exp $ip free
done
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
Shell 嵌套 Expect
创建密钥

本地创建密钥

#!/bin/bash 
echo "===========genrsa====================" 
expect <<EOF 
spawn ssh-keygen -t rsa 
expect { 
"*id_rsa):" { 
send "\n"; 
exp_continue 
} 
"*(y/n)?" { 
send "y\n" 
exp_continue 
} 
"*passphrase):" { 
send "\n" 
exp_continue 
} 
"*again:" { 
send "\n" 
} 
} 
expect eof 
EOF
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
function fn_create_secret_key ()
{
 USER=$1
 PASSWD=$2
 MG_IP_LIST=$3
 for MG_IP in $MG_IP_LIST
 do
  ssh-keygen -R $MG_IP
  /usr/bin/expect<<EOF
  set timeout -1
  spawn ssh ${USER}@$MG_IP "rm -rf ~/.ssh/*"
  expect {
      "*assword:" { send "$PASSWD\r" }
      "*yes/no" { send "yes\r"; exp_continue}
      "*assword:" { send "$PASSWD\r" }
     }
  spawn ssh ${USER}@$MG_IP "ssh-keygen -t rsa -f ~/.ssh/id_rsa -N ''"
  expect {
      "*assword:" { send "$PASSWD\r" }
      "*yes/no" { send "yes\r"; exp_continue}
      "*assword:" { send "$PASSWD\r" }
     }
    expect eof
EOF
 done
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
传输密钥

单机传输

vi send_key.exp
#!/usr/bin/expect

set timeout 10
set user_hostname [lindex $argv 0]
set password [lindex $argv 1]
spawn ssh-copy-id $user_hostname
    expect {
        "(yes/no)?"
        {
            send "yes\n"
            expect "*assword:" { send "$password\n"}
        }
        "*assword:"
        {
            send "$password\n"
        }
    }
expect eof

#执行
expect send_key.exp 10.8.17.84 y4yhljT#
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

批量传输

vi sci.sh
#!/bin/bash

passwd=y4yhljT#               # 指定要传递的密码为123456
user_host=`awk '{print $3}' ~/.ssh/id_rsa.pub`   # 此变量用于判断远程主机中是否已添加本机信息成功
 
for i in $@  
do
        expect send_key.exp $i $passwd >&/dev/null
        ssh $i "grep "$user_host" ~/.ssh/authorized_keys" >&/dev/null  # 判断是否添加本机信息成功
        if [ $? -eq 0 ];then
                echo "$i is ok"
        else
                echo "$i is not ok"
        fi
done

#执行
bash sci.sh 10.8.17.85 10.8.17.86
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
#*define transport security key
function fn_single_transfer_secret_key ()
{
 USER=$1
 PASSWD=$2
 MG_IP_LIST=$3
 for MG_IP in $MG_IP_LIST
 do
  /usr/bin/expect<<EOF
  set timeout -1
  spawn ssh-copy-id -i ~/.ssh/id_rsa.pub ${USER}@$MG_IP
  expect {
      "*assword:" { send "${PASSWD}\r" }
      "*yes/no" { send "yes\r"; exp_continue}
      "*assword:" { send "${PASSWD}\r" }
     }
      expect eof
EOF
done
}

#define single security key
function fn_single_secret_key ()
{
 USER=$1
 PASSWD=$2
 MG_IP_LIST=$3
 fn_create_secret_key "$USER" "$PASSWD" "$MG_IP_LIST"
 fn_single_transfer_secret_key "$USER" "$PASSWD" "$MG_IP_LIST"
}
 
#start fn_single_secret_key
fn_single_secret_key root 123456 10.172.10.10
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
Logo

一站式 AI 云服务平台

更多推荐

  • 浏览量 1267
  • 收藏 0
  • 0

所有评论(0)

查看更多评论 
已为社区贡献3条内容