本文目录导读:

我来详细讲解 Expect 自动化交互脚本的编写方法和常用模式。
基础结构
最简单的 Expect 脚本模板
#!/usr/bin/expect -f
# 设置超时时间
set timeout 10
# 启动程序
spawn ssh user@host
# 期待模式并发送响应
expect "password:" {
send "mypassword\r"
}
# 继续交互或退出
expect "$ "
send "ls -la\r"
# 等待命令完成
expect "$ "
# 结束交互
send "exit\r"
expect eof
关键命令详解
spawn - 启动进程
spawn ssh user@host spawn ftp ftp.example.com spawn telnet 192.168.1.1
expect - 等待模式匹配
# 基本用法
expect "pattern"
# 超时处理
expect {
"pattern1" { send "response1\r" }
"pattern2" { send "response2\r" }
timeout { puts "超时了"; exit 1 }
eof { puts "连接关闭"; exit }
}
# 全局匹配
expect -re {[Pp]assword:}
expect -exact "exact string"
send - 发送响应
send "command\r" # 发送命令并回车 send -- "password\r" # 发送以 - 开头的字符串 send -s "slow\r" # 缓慢发送
完整实例
SSH 自动登录和执行命令
#!/usr/bin/expect -f
# 定义变量
set host [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set timeout 30
# 检查参数
if { $argc != 3 } {
puts "Usage: $argv0 host user password"
exit 1
}
# 启动 SSH
spawn ssh -o StrictHostKeyChecking=no $user@$host
# 处理认证
expect {
# 第一次连接时的主机密钥确认
"(yes/no)?" {
send "yes\r"
exp_continue
}
# 密码提示
"password:" {
send "$password\r"
exp_continue
}
# 登录成功后的提示符
-re {[$#] } {
puts "登录成功!"
}
timeout {
puts "连接超时"
exit 1
}
}
# 执行命令
send "uptime\r"
expect -re {[$#] }
send "who\r"
expect -re {[$#] }
# 退出
send "exit\r"
expect eof
puts "完成!"
交互式程序控制
#!/usr/bin/expect -f
# 自动配置程序
proc configure_app {password} {
spawn ./setup.sh
expect {
"Enter password:" {
send "$password\r"
exp_continue
}
"Confirm password:" {
send "$password\r"
exp_continue
}
"Choose option (1-5):" {
send "2\r"
exp_continue
}
"Continue? (y/n):" {
send "y\r"
exp_continue
}
"Installation complete" {
puts "安装完成"
}
timeout {
puts "安装超时"
exit 1
}
eof {
puts "程序结束"
}
}
}
configure_pass "mySecret123"
文件传输自动化
#!/usr/bin/expect -f
proc safe_ftp {host user password local_file remote_dir} {
spawn ftp $host
expect "Name (*):" {
send "$user\r"
}
expect "Password:" {
send "$password\r"
}
expect "ftp>" {
send "cd $remote_dir\r"
}
expect "ftp>" {
send "binary\r"
}
expect "ftp>" {
send "put $local_file\r"
}
expect {
-re {bytes sent in} {
puts "文件上传成功"
}
timeout {
puts "上传超时"
}
}
expect "ftp>" {
send "quit\r"
}
expect eof
}
safe_ftp "ftp.example.com" "user" "pass" "test.txt" "/uploads"
高级技巧
错误处理
#!/usr/bin/expect -f
proc ssh_with_retry {host user password {retry 3}} {
set count 0
while {$count < $retry} {
spawn ssh -o StrictHostKeyChecking=no $user@$host
expect {
"password:" {
send "$password\r"
expect {
-re {[$#] } {
puts "连接成功"
return 1
}
"Permission denied" {
puts "密码错误"
close
incr count
continue
}
timeout {
puts "登录超时"
close
incr count
continue
}
}
}
timeout {
puts "连接超时"
close
incr count
continue
}
eof {
puts "连接被拒绝"
close
incr count
continue
}
}
}
puts "重试次数用尽"
return 0
}
if {[ssh_with_retry "server.com" "admin" "pass123"]} {
send "ls -la\r"
expect -re {[$#] }
send "exit\r"
expect eof
}
多主机并行处理
#!/usr/bin/expect -f
proc process_host {host user password} {
spawn ssh $user@$host
expect "password:" {
send "$password\r"
}
expect -re {[$#] }
send "hostname\r"
expect -re {[$#] }
send "df -h\r"
expect -re {[$#] }
send "free -m\r"
expect -re {[$#] }
send "exit\r"
expect eof
}
# 从文件读取主机列表
set f [open "hosts.txt" r]
set timeout 20
while {[gets $f line] >= 0} {
set fields [split $line]
set host [lindex $fields 0]
set user [lindex $fields 1]
set pass [lindex $fields 2]
puts "处理主机: $host"
process_host $host $user $pass
}
close $f
日志记录
#!/usr/bin/expect -f
# 开启日志
log_file -a "expect_auto.log"
log_user 1 # 显示输出
spawn ssh user@host
expect "password:" {
send "mypassword\r"
}
# 记录到文件
set logfile [open "session.log" w]
expect_before {
-re {.*} {
puts $logfile $expect_out(buffer)
}
}
expect -re {[$#] }
send "cat /etc/passwd\r"
expect -re {[$#] }
close $logfile
send "exit\r"
expect eof
log_file
实战:自动化部署脚本
#!/usr/bin/expect -f
set timeout 30
set APP_DIR "/opt/myapp"
proc deploy {host user password version} {
spawn ssh $user@$host
expect "password:" {
send "$password\r"
}
expect -re {[$#] }
# 备份
send "sudo cp -r $APP_DIR ${APP_DIR}_backup_\$(date +%Y%m%d)\r"
expect "password for" {
send "$password\r"
}
expect -re {[$#] }
# 下载新版本
send "cd /tmp && wget http://repo.example.com/app-v${version}.tar.gz\r"
expect -re {[$#] }
# 解压并部署
send "tar -xzf app-v${version}.tar.gz -C $APP_DIR\r"
expect -re {[$#] }
# 重启服务
send "sudo systemctl restart myapp\r"
expect "password for" {
send "$password\r"
}
expect -re {[$#] }
# 检查状态
send "sudo systemctl status myapp\r"
expect -re {[$#] }
send "exit\r"
expect eof
puts "部署完成!版本: $version"
}
# 执行部署
if {$argc >= 3} {
deploy [lindex $argv 0] [lindex $argv 1] [lindex $argv 2] [lindex $argv 3]
} else {
puts "用法: ./deploy.tcl host user password version"
exit 1
}
常用模式和最佳实践
模式匹配技巧
# 模糊匹配
expect -gl {*password*}
expect -regexp {[Pp]ass(word|wd):}
# 匹配多个模式
expect {
"success" { do_success }
"failure" { do_failure }
"error" { do_error }
default { do_default }
}
# 时间相关的匹配
expect {
-timeout 5 "fast"
timeout { puts "等待超时" }
}
调试技巧
#!/usr/bin/expect -d # 开启调试模式 # 或者在脚本中开启 exp_internal 1 # 显示内部调试信息 # 手动控制输出 log_user 0 # 关闭输出 # ... 执行敏感操作 ... log_user 1 # 恢复输出
这些示例涵盖了 Expect 的主要用法,关键是要理解:
- 并行思维:处理可能出现的多种情况
- 适当超时:为每个操作设置合理的超时时间
- 错误处理:处理密码错误、连接超时等常见问题
- 安全性:避免在命令行直接暴露密码
根据你的具体需求调整这些模板即可。