目录

使用expect来解决命令交互问题

linux里面很多命令都是需要人为交互的,对于做成脚本来说,有点不合适了,比如通过密码连接SSH必须要在控制台输入密码(安全起见还是用rsa key),expect是预期的意思,它可以实现我们预期的结果。

安装

  • ubuntu/debian

    sudo apt install -y expect
    
  • centos/rhel

    sudo yum install -y ecpect
    

解释器使用expect

#!/usr/bin/expect

set IP     [lindex $argv 0] # 读取第1个参数设置为 IP 变量
set PASSWD [lindex $argv 1] # 读取第2个参数设置为 PASSWD 变量
set CMD    [lindex $argv 2] # 读取第3个参数设置为 CMD 变量

spawn ssh $IP $CMD # spawn 来给命令加壳,以便于断言输出
expect { # expect 是断言命令
  # 如果读取到屏幕上输出 (yes/no) 信息,则输入 "yes" 并按下回车键
  # exp_continue 是继续等待花括号内的断言, 如果不加这一句会直接跳出 expect
  "(yes/no)?" { send "yes\r"; exp_continue }

  "password:" { send "$PASSWD\r" } # 如果读取到屏幕上输出 password 信息,则输入 PASSWD 变量中的内容
  "*host " { exit 1 } # 如果读取到 "No route to host" 等内容, 就以非0状态退出
}
expect eof # 等待命令执行结束

这种方式由于解释器使用了expect,所以只能使用有限的命令,不是很推荐

解释器使用bash

假设certbot不支持非交互使用

#!/bin/bash

sudo expect << EOF
spawn certbot --nginx
expect {
  "Enter email address" { send "iuxt@qq.com\n";exp_continue}
  "Please read the Terms of Service" {send "A\n";exp_continue}
  "Would you be willing to share your email address" {send "N\n";exp_continue}
  "Which names would you like to activate HTTPS for" {send "\n";exp_continue}
  "You have an existing certificate that has exactly the same domains" {send "1\n";exp_continue}
  "Please choose whether or not to redirect HTTP traffic to HTTPS" {send "2\n";exp_continue}
  eof
}

直接使用expect命令

这种和切换解释器类似, 类比于./test.shbash test.sh的关系。

vim test.sh

spawn ldapadd -x -D cn=Manager,dc=nutstore,dc=com -W -f /vagrant/basedomain.ldif
expect {
"Enter LDAP Password:" {send "com.012\n";exp_continue}
eof
}
expect test.sh