Expect 能做什么?
Expect 是由 Don Libes 基于 Tcl(Tool Command Language) 脚本语言编写的一个扩展,常被用来处理程序的自动交互。如:SSH 的自动登录、FTP 登录下载、passwd 修改密码…等类似需要人机交互的场景,都可以用 Expect 来实现自动化处理。详情可阅读 Expect - Wikipedia 和 Expect - Linux man page。
Tcl(Tool Command Language) 作为一个可跨平台的胶水脚本语言,它可以像 Shell 一样,但功能又比 Shell 要强大一些,我们可以在 Expect 脚本中使用 Tcl 语法实现一些复杂的操作。详细的教程在这里不做赘述,可在文章末尾的 阅读参考 中获取相应资料进行学习,非常的简单。
Expect 基础
在 Expect 脚本中,最常用到的几个命令:spawn
、expect
、send
、interact
。
Command | Description |
---|---|
spawn | 启动一个进程 |
expect | 接收进程的标准输出 |
send | 向进程标准输入信息 |
interact | 将控制权交给用户进行交互 |
puts | 用于向执行当前 Expect 的进程输出信息 |
设置变量及访问变量
可以通过 set <VARIABLE> <VALUE>
的方式进行设置变量及其变量值。
若你是通过命令行执行 Expect 脚本(*.exp
),在执行时候可通过命令行传入参数,在 Expect 脚本中可以通过 set <VARIABLE> [lindex $argv 0/1/2...]
的方式接收。
注意:Expect
脚本和 Shell
脚本都是通过 $0…$n 的方式来接收命令参数,但是在 Shell
中 $0 标识脚本自身,而在 Expect
中则表示第一个参数。
Demo
方式1:自动输入密码登录
#!/usr/bin/bash
USER="login user"
HOST="my host or ip"
PORT="port"
PWD="login password"
# 判断 ssh、expect 命令是否存在
command -v ssh > /dev/null 2>&1 && {
command -v expect > /dev/null 2>&1 && {
# 通过 expect 自动执行命令
expect -c "
set timeout 15;
puts \"[lindex $argv 1]\n\"
spawn ssh -l $USER $HOST -p $PORT;
expect {
\"*yes/no*\" { send \"yes\r\"; exp_continue; }
\"*password:*\" { send \"$PWD\r\"; }
};
interact
"
} || {
echo "Command 'expect' not found. Try: 'sudo apt-get install expect' or 'sudo yum install expect' to install it.\n"
}
} || {
echo "Command 'ssh' not found!\n"
}
保存上述脚本,然后在 ~/.bashrc
或 ~/.bash_aliases
中添加命令别名
# 将 expect 脚本存放目录添加到环境变量中
export EXPECT_PATH="/usr/local/bin/expect"
export PATH="$PATH;$EXPECT_PATH"
# 设置命令别名
alias _ssh_erc_jumpserver="/$EXPECT_PATH/_ssh_erc_jumpserver
方式2:通过 expect 脚本实现
确保本机已经安装了 expect 扩展,然后将以下脚本保存到 /usr/local/bin/_ssh
中,并通过 chmod +x /usr/local/bin/_ssh
赋予可执行权限。后续就可以通过 _ssh dev_web
、_ssh dev_mysql
自动登录了。
#!/usr/bin/expect -f
log_user 0; # 屏蔽脚本的输出,缺省值为 1
set timeout 15; # 设置超时时间
set SERVER_TYPE [lindex $argv 0]; # 接收参数并将第一个参数赋值给 SERVER_TYPE
# 自定义函数-成功回调
proc expect_success { SERVER } {
switch $SERVER {
dev_web {
# 测试环境 web 机
set USER "login user"
set HOST "server host or ip"
set PORT "port"
set PWD "password"
}
dev_mysql {
# # 测试环境 MySQL
set USER "login user"
set HOST "server host or ip"
set PORT "port"
set PWD "password"
}
dev_mc {
# 测试环境缓存机器
set USER "login user"
set HOST "server host or ip"
set PORT "port"
set PWD "password"
}
dev_proxy {
# 测试机 Nginx 入口机
set USER "login user"
set HOST "server host or ip"
set PORT "port"
set PWD "password"
}
erc_jumpserver {
# JumpServer 机器
set USER "login user"
set HOST "server host or ip"
set PORT "port"
set PWD "password"
}
default {
puts "Unknown Server!"
exit
}
}
# 启动进程执行 ssh 连接到服务器
spawn ssh -l $USER $HOST -p $PORT;
expect {
"*yes/no*" { send "yes\r"; exp_continue; }
"*password:*" { send "$PWD\r"; }
}
# 将控制权交还给用户进行交互
interact
}
# 终止函数
proc exit_expect { } {
puts "Command 'ssh' not found! \nTry: 'sudo apt-get install openssh' or 'sudo yum –y install openssh-server openssh-clients' to install it."
exit
}
# 校验是否安装 ssh 服务
spawn bash -c "command -v ssh > /dev/null 2>&1 && echo $? || echo $?"
expect {
1 {
exit_expect
}
0 {
expect_success $SERVER_TYPE
# expect_success $erc_web
}
}
如何确定一个环境变量是在哪个脚本中设置的??
大部分发行版的 Linux 启动后环境变量加载的顺序为:etc/profile
→ /etc/profile.d/*.sh
→ ~/.bash_profile
→ ~/.bashrc
→ [/etc/bashrc
]。
部分发行版的系统可能会不一致,例如:WSL Ubuntu 就是先加载了 /etc/bash.bashrc
再加载 /etc/profile.d/*.sh
,但不管如何都一定是从 etc/profile
开始。
暂时没有很好的办法进行一步到位的确定,只能挨个找。