Expect脚本失败主因是匹配不到输出,需用绝对路径spawn、显式设环境变量、用-re匹配密码提示、send后加 、设足够timeout及match_max。

Expect 脚本不是“写完就能跑”,关键在于 spawn 启动进程后,expect 是否真能捕获到目标字符串——很多失败根本不是语法错,而是匹配不到输出。
spawn 启动命令前必须确认路径和权限
spawn 不是 shell,它不走 $PATH 查找,也不自动继承当前 shell 的环境变量。比如你本地 which scp 返回 /usr/bin/scp,但 spawn 直接写 spawn scp ... 很可能报 couldn't execute "scp": no such file or directory。
- 始终用绝对路径:如
spawn /usr/bin/scp file user@host:/path - 如果 spawn 的是自定义脚本,确保它有执行权限(
chmod +x),且首行#!/bin/bash正确 - spawn 后的进程若依赖某些环境变量(如
LANG、HOME),需在 spawn 前用set env(LANG) "C"显式设置,否则 expect 可能因乱码或提示语差异而匹配失败
expect 匹配失败的三个高频原因
expect 不是模糊搜索,它严格按缓冲区内容逐字节比对。常见“明明看到了密码提示却卡住”的情况,基本都出在这儿:
- 提示符里有空格或不可见字符:比如实际输出是
Password:(末尾带空格),但你写expect "Password:"就会超时。应改用expect -re "Password:.*"或更稳妥的expect "*[Pp]assword:*" - 输出被换行或回车截断:某些程序(如老版本 ssh)会在密码提示后立即输出一个
,导致 expect 缓冲区里是"Password: "。此时用expect "Password:"仍不匹配,得写expect "Password:.*"或启用-re - 超时太短或没设:默认
timeout是 10 秒,但网络延迟、远程主机负载高时,ssh 连接或 banner 输出可能超过 10 秒。务必在脚本开头加set timeout 30(或-1表示永不超时,仅调试用)
send 发送密码后必须处理换行和回车
send 不自动加换行,而绝大多数交互式程序(ssh、sftp、passwd)都要求输入后跟
(不是
)。写成 send "mypass" 会导致密码发出去但没回车,程序一直等。
- 一律用
send "your_password ",注意是,不是或\r - 如果密码含特殊字符(如
$、[、]),需用大括号包裹避免 Tcl 解析:send {$password } - 发送后别立刻 expect 提示符,先加个
sleep 0.5(Tcl 的after 500)让远程端有时间响应,尤其在低速链路或老旧设备上
interact 和 eof 的选择决定脚本是否干净退出
脚本最后用 interact 还是 expect eof,取决于你是否需要接管后续交互。多数自动化场景应避免 interact,否则脚本不会自动退出,卡在终端里。
- 只做单次操作(如传一个文件、跑一条命令):结尾用
expect eof,它会等待子进程真正关闭 - 需要人工介入后续操作(如登录后手动查日志):才用
interact,但要加trap或注释说明,否则运维同学会误以为脚本卡死 - 如果 spawn 的是 ssh 且执行了
exit,但脚本仍 hang 住,大概率是远程 shell 没真正退出(比如有后台 job),此时可在 send "exit " 后加expect eof,而不是依赖interact
最易被忽略的是 expect 缓冲区大小限制:默认只缓存最近 2000 字节输出,如果远程命令输出很长(如 ps aux 或日志 dump),早先的提示符可能已被挤出缓冲区,导致匹配永远失败。调试时加 set match_max 100000 能快速验证是不是这个原因。

















