=======================================
关于expect
date:2023-05-18
=======================================
参考文档:
linux expect 详解_linux_初码诛仙-DevPress官方社区.mhtml
=======================================
解释:expect 用于linux环境下,自动交互,如shell执行,需要手动需输入密码场景
expect 需要安装
- 1、命令速查
spawn:交互程序开始后面跟命令或者指定程序(在壳内启动这个进程)
expect:获取匹配信息匹配成功则执行expect后面的程序动作(检测由壳内进程发出的特定交互指令反馈字符串后向下执行)
expect eof:退出expect
expect_out:获取expect输出字符
send:用于向进程发送字符串(从壳外向壳内进程发送一条字符串,换行符为确认结束)
interact:允许用户交互
exp_continue:在expect中多次匹配就需要用到
send_user:用来打印输出 相当于shell中的echo
send_error:用来打印输出 相当于shell中的echo ( 貌似和send_user 没区别一样 )
exit:退出expect脚本
eof:expect执行结束 退出
set:定义变量
puts:输出变量
set timeout:设置超时时间
- 2、常用命令解析
如下将expect 嵌套于shell 中。
2.1、解释器
使用expect时,需要expect解释器,expect代码包含在
/usr/bin/expect <<-EOF #注意这个 “ - ”
expect代码
EOF
2.2、spawn & expect
spawn:
启动一个命令
spawn "cmd"
expect:
监控spawn命令执行结果输出,去匹配关键字,然后动作。
expect { #这里中括号必须与expect同一行
"关键字1" {动作1}
"关键字2" {动作2}
"关键字N" {send "yes\r";exp_continue}
}
Tips:如果expect对所有的关键字匹配不上会出现超时报错
#expect: spawn id exp4 not open
#while executing
#"expect eof"
2.2.1 expect输出值获取
expect " " {
set raw $expect_out(buffer)
# remove final carriage return
set response [string trimright "$raw" " "]
}
2.2.2 expect 判断语句
if {"$response" == ""} {set response $def}
2.3、send
命令发送,配合expect匹配关键字后使用
*注意send 输出,也可以被expect监控,可用于send执行结果判断
2.4、send_user、send_error
相当于shell 的echo
换行符 \r \n
\r: 回到本行开头 (在send 命令表示回车)
\n: 换行、回车
使用\n 换行, \r换行会导致当前行不显示
EXP:
******
expect {
"*Permission denied, please try again*" {send_user "\n***********root密码错误,退出当前连接!\n";exit 1}
}
2.5、exp_continue
类似for、while中的continue
2.6、参数传递
用于expect是独立脚本情况
expect嵌套在shell 中时,可直接条用shell中变量
argv[n]: 参数
argc: 表示参数的数量
#!/usr/bin/expect
set uname [lindex $argv 0]
set pwd [lindex $argv 1]
puts "$argc"
2.7、timeout
设置超时
set timeout 30
超时调用
timeout {send_error "\n处理超时\n";exit 1}
2.99 实例代码'ChangePasswd') { ( /usr/bin/expect <<-EOF set timeout 30 #spawn bash -c "echo 'logoin ${1}'" spawn bash -c "ssh ${SYSTEM_USER}@${1} ${3}" expect { "*yes/no*" {send "yes\r";exp_continue} "*password*" { send "${SYSTEM_PASSWD}\r" expect { "*Permission denied, please try again*" {send_user "\n***********root密码错误,退出当前连接!\n\n";exit 1} "*password*" { send "${SYSTEM_PASSWD_NEW}\r" expect "*password*" {send "${SYSTEM_PASSWD_NEW}\r"} } } } } #send "exit\r\r" expect eof EOF ) #/dev/null 2>&1 }
- 3、关于expect
3.1 关于expect: spawn id exp4 not open 报错
要判断完整,比如下方如果只判断密码错误,就会导致匹配超时
这里再匹配一个无任何输出的情况,注意" " 中间是空格
send "${SYSTEM_PASSWD}\r"
expect {
"*Permission denied, please try again*" {send_user "\n***********root密码错误,退出当前连接!\n\n";exit 1}
" " {send_user "\n获取成功\n"}
# 用于通配匹配, 如果不加这个匹配,上面单独一行时会匹配超时,对于没有空格输出的命令还是会报错,怎么处理呢?
# 不能使用"*"通配
#导致expect匹配超时报错expect: spawn id exp4 not open
#while executing
#"expect eof"
}
10、实例代码
#!/bin/bash
# Function: 对远程服务器执行命令、mysql、systemcmd
# Author: ls
# Date:2023-04-27
SYSTEM_USER='root'
SYSTEM_PASSWD='111'
SYSTEM_PASSWD_NEW='2222'
#========== Servers list===============
server_list=(
'2 192.168.100.110 S1 code1 22' '3 192.168.100.21 S2 code2 22' '4 192.168.100.11 S3 code3 22'
)
#===========================================system control===========================================================
function SYSTEM_CHECK_TOOLS_expect(){
echo "check expect install ...."
if type expect >/dev/null 2>&1;then
echo .
echo "expect has been installed ...."
echo .
else
{
echo "expect not install,install expect now ..."
echo "*************************"
echo "============================"
echo "Install Tool expect online"
echo "============================"
sudo apt --fix-broken install -y
sudo apt-get install expect -y || exit 0
}
fi
}
# ssh 登录函数,使用expect 交互
# $1 远程服务器地址 $2 选项 $3 需要执行的命令
function SYSTEM_COMMAND_remote_ssh_logoin_runcmd()
{
if [ $# != 3 ];then
{
echo '参数错误,exit;'
exit
}
fi
case ${2} in
'ChangePasswd')
{
(
/usr/bin/expect <<-EOF
set timeout 30
#spawn bash -c "echo 'logoin ${1}'"
spawn bash -c "ssh ${SYSTEM_USER}@${1} ${3}"
expect {
"*yes/no*" {send "yes\r";exp_continue}
"*password*"
{
send "${SYSTEM_PASSWD}\r"
expect {
"*Permission denied, please try again*" {send_user "\n***********root密码错误,退出当前连接!\n\n";exit 1}
"*password*"
{
send "${SYSTEM_PASSWD_NEW}\r"
expect "*password*" {send "${SYSTEM_PASSWD_NEW}\r"}
}
}
}
}
#send "exit\r\r"
expect eof
EOF
)
#/dev/null 2>&1
}
;;
"shell")
{
#(
/usr/bin/expect <<-EOF
set timeout 30
spawn bash -c "(ssh ${SYSTEM_USER}@${1}<${3}|tail -n 5)"
expect {
"*yes/no*" {send "yes\r";exp_continue}
"*password*"
{
send "${SYSTEM_PASSWD}\r"
expect {
"*Permission denied, please try again*" {send_user "\n***********root密码错误,退出当前连接!\n\n";exit 1}
" " {send_user "\n获取成功\n"}
# 用于通配匹配, 如果不加这个匹配,上面单独一行时会匹配超时,对于没有空格输出的命令还是会报错,怎么处理呢?
# 不能使用"*"通配
#导致expect匹配超时报错expect: spawn id exp4 not open
#while executing
#"expect eof"
}
}
}
#send "exit\r\r"
expect eof
EOF
#)2>>/dev/null
#>>/dev/null 2>&1
#>>/tmp/serinfo.md 2>&1
}
;;
'cmd')
{
#(
/usr/bin/expect <<-EOF
set timeout 20
spawn bash -c "ssh ${SYSTEM_USER}@${1} ${3}"
expect {
"*yes/no*" {send "yes\r";exp_continue}
"*password*"
{
send "${SYSTEM_PASSWD}\r"
expect {
"*Permission denied, please try again*" {send_user "\n***********root密码错误,退出当前连接!\n\n";exit 1}
" " {send_user "\n获取成功\n"}
}
}
}
send "\r\r"
expect eof
EOF
#)>>/dev/null 2>&1
}
;;
*)
{
echo 'Usege:'
echo '# $1 远程服务器地址 $2 选项 $3 需要执行的命令'
echo 'SYSTEM_COMMAND_remote_ssh_logoin_runcmd [ip] [OPTION] [cmd]'
echo 'OPTION:'
echo ' ChangePasswd 修改账户密码, EXP: SYSTEM_COMMAND_remote_ssh_logoin_runcmd ChangePasswd 'sudo passwd root' '
echo ' shell 远程执行脚本, EXP:SYSTEM_COMMAND_remote_ssh_logoin_runcmd shell ls_server_info.sh'
echo ' cmd 远程执行普通命令, EXP:SYSTEM_COMMAND_remote_ssh_logoin_runcmd 'df -h''
echo ' ... 待实现...'
}
;;
esac
}
# 主函数
# $1 远程服务器地址 $2 需要执行的命令
function SYSTEM_COMMAND_remote_ssh()
{
SYSTEM_CHECK_TOOLS_expect
for ((i=0;i<${#server_list[@]};i++))
do {
array_tmp=(${server_list[i]})
echo "============== ${array_tmp[0]} - ${array_tmp[2]}====================="
#SYSTEM_COMMAND_remote_ssh_logoin_runcmd ${array_tmp[1]} ChangePasswd "sudo passwd root"
#SYSTEM_COMMAND_remote_ssh_logoin_runcmd ${array_tmp[1]} cmd 'df -h'
SYSTEM_COMMAND_remote_ssh_logoin_runcmd ${array_tmp[1]} shell 'ls_server_info.sh'
}
done
}
#===========================================system control===========================================================
#===========================================function main ===========================================================
function MAIN()
{
#MYSQL_LS_DB_GET_DATA
SYSTEM_COMMAND_remote_ssh
}
MAIN
#===========================================function main ===========================================================
exit 0