expect 自动交互

当运行一个命令时(如 ssh 登录),我们必须与它进行交互,程序才能继续运行;但是在写 shell 脚本时很不方便,无法做到自动化,这时候就该 expect 出场了!

安装

## RHEL/CentOS
yum -y install expect

## ArchLinux
pacman -S --need expect

语法

expect 的核心指令分别是:spawnexpectsend

1) spawn,包装一个子进程,即:给子进程加壳。
2) expect,监听子进程的 stdout/stderr,如果匹配成功,则执行定义的命令;
3) send,将给定的数据写入子进程的 stdin 文件。

timeout 超时设置
默认为 10s,可以通过set timeout 120来设置超时时间(单位:秒),如果设为 -1,则永不超时。
这个超时时间用在哪呢?在执行 expect 语句时,需要匹配子进程的输出,如果在给定时间内未匹配,则直接执行下一条语句。

regex 正则匹配
默认 expect 语句使用 shell 通配符匹配,有时候无法满足我们的要求。这时候就可以使用-re选项开启正则匹配。
用法:expect -re "regex"expect { -re "regex" { send "str\r" } }

例子 1

#!/usr/bin/expect

set timeout 30                  # 设置超时
set host "192.168.255.104"      # 定义变量
set user "root"                 # 定义变量
set passwd "123456"             # 定义变量

spawn ssh $user@$host           # 启动子进程

expect {
    "*password*" {
        send "$passwd\r"        # 若匹配到 password,则输入 $passwd 的值
    }

    "*connecting*" {
        send "yes\r"            # 若匹配到 connecting 则输入 yes

        exp_continue            # 继续匹配

        "*password*" {
            send "$passwd\r"    # 若匹配到 password 则输入 $passwd 的值
        }
    }
}

interact                        # 保持交互状态,即将控制权交回给我们

例子 2

#!/usr/bin/expect

if { $argc < 3 } {  # argc 表示参数个数
    puts "Usage: cmd <host> <username> <password>"
    exit 1          # 退出当前进程
}

set timeout -1              # 永不超时
set host [lindex $argv 0]   # 第一个参数
set user [lindex $argv 1]   # 第二个参数
set passwd [lindex $argv 2] # 第三个参数

spawn ssh $user@$host

expect {
    "*password*" {
        send "$passwd\r"
    }

    "*connecting*" {
        send "yes\r"

        exp_continue

        "*password*" {
            send "$passwd\r"
        }
    }
}

interact

例子 3

#!/usr/bin/expect

if { $argc < 4 } {
    puts "Usage: cmd <file> <host> <username> <password>"
    exit 1
}

set timeout -1
set file [lindex $argv 0]
set host [lindex $argv 1]
set user [lindex $argv 2] 
set passwd [lindex $argv 3]

spawn scp $file $user@$host:

expect {
    "*password*" {
        send "$passwd\r"
    }

    "*connecting*" {
        send "yes\r"

        exp_continue

        "*password*" {
            send "$passwd\r"
        }
    }
}

expect "eof" # 匹配 "eof",它的作用:
# 因为 timeout = -1,因此,理论上不会因为超时而退出程序;
# 不过,当子进程运行完毕,退出时,expect 脚本会自动退出;
# 这样做的目的是为了让子进程能够正确完成任务,而不会意外的被结束。
# "eof" 只是一个普通字符串,你可以选择任意不会出现的字符串替代它!