Shell脚本使用expect实现免交互命令
前言
最近研究了一下SSH远程免密登录,也遇到了一些问题,比如远程登录成功后执行脚本的时候,如何自动输入密码,如何进行其他的需要手动输入信息的交互式操作,有两种解决方案,如
echo 'xxx'| sudo script
这样的管道式操作,还有
!# /usr/bin/bash
sudo xxx <<EOF
xxx
xxx
EOF
这类的重定向方法,但是对于类似su user
这样的操作会报错
standard in must be a tty
stdin
(标准输入设备)必须是一个tty
(虚拟终端,就是人机交互的接口),最终被我找到了一个很好的解决方案,就是使用expect
来实现交互式操作。Expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。
安装
# CentOS安装
yum install expect
# Ubuntu安装
sudo apt-get install expect
安装成功后如下图所示:
参数说明
set
:可以设置超时,也可以设置变量,然后在脚本中可以使用$变量
或者${变量}
使用timeout
:expect
超时等待时间,默认10Sspawn
:执行一个Shell
命令expect ""
:对通过spawn
执行的shell
脚本的返回进行判断,是否包含""
中的字段exp_continue
:继续执行下面匹配\r
:可以理解为回车$argc
:统计位置参数数量[lindex $argv 0]
:脚本后第一个参数,类似于shell中$1
,以此类推puts
:打印字符串,类似于echoawk -v I="$ip"
:赋值变量expect{...}
:输入多行记录timeout -1
:永不超时退出expect eof
:等待执行结束,若没有这一句,可能导致命令还没执行,脚本就结束了log_file /var/log/expect.log
:记录交互信息,一般crontab
时使用interact
:退出expect
返回终端,可以继续输入,否则将一直在expect不能退出到终端if{} {}elif{}{}else{}
(注意if与花括号,花括号和花括号之间都需要有空格,else
不能单独放一行,所以else要跟在}
后面)
经过我实际测试,一个expect脚本只能有一个spawn执行命令,如果还要执行其他的shell命令的话,直接使用send就可执行shell命令
简单实例
- 切换用户创建测试文件夹
#!/usr/bin/expect
set timeout 30
set passwd "xxxxxxx"
spawn su root
expect "Password:"
send "$passwd\r"
send "cd ~\r"
send "mkdir test\r"
interact
expect eof
exit
- 实现远程登录服务器,并切换到root用户下执行关闭防火墙的命令,然后退出
#!/usr/bin/expect
if {$argc < 4} {
#do something
send_user "usage: $argv0 <remote_user> <remote_host> <remote_pwd> <remote_root_pwd>"
exit
}
set timeout -1
set remote_user [lindex $argv 0] # 远程服务器用户名
set remote_host [lindex $argv 1] # 远程服务器域名
set remote_pwd [lindex $argv 2] # 远程服务器密码
set remote_root_pwd [lindex $argv 3] # 远程服务器根用户密码
# 远程登录
spawn ssh ${remote_user}@${remote_host}
expect "*assword:" {send "${remote_pwd}\r"}
expect "Last login:"
# 切换到 root
send "su\r"
expect "*assword:" {send "${remote_root_pwd}\r"}
# 执行关闭防火墙命令
send "service iptables stop\r"
send "exit\r"
send "exit\r"
expect eof