expect脚本执行前必须确认三件事:which expect有路径、tcl已安装、首行是#!/usr/bin/expect且有执行权限;匹配需贴合真实输出,send后必跟expect等待响应;timeout应分层设置而非-1。

expect 脚本不是“写完就能跑”,它依赖 tcl 运行时、匹配逻辑必须贴合真实输出、超时和换行处理稍有偏差就会卡住或跳过关键步骤——直接用默认配置跑 SSH 登录,八成会失败。
expect 脚本执行前必须确认的三件事
缺一不可,否则脚本启动就报错或静默退出:
-
which expect必须返回路径(如/usr/bin/expect),没安装就先yum install expect或apt-get install expect; -
tcl是底层依赖,expect安装通常会自动拉取,但若报can't find package Tcl,需手动补装yum install tcl; - 脚本首行必须是
#!/usr/bin/expect(不能写成#!/usr/bin/env expect,部分系统不认),且用chmod +x script.exp加执行权限。
spawn 启动命令后,expect 匹配不到提示符的常见原因
不是正则写错了,而是输出内容和你想象的不一致。比如 ssh user@host 实际可能先输出 The authenticity of host 'xxx' can't be verified.,再输出 password:,但脚本只写了 expect "password:",就会卡在第一句上。
Linux系统管理专家,覆盖12大模块:用户权限、SSH、存储、网络、systemd、防火墙、日志监控、备份恢复、TLS证书、Ansible、容器、IaC。提供配置、验证、加固、监控、备份、自动化、故障排查、回滚闭环。关键词:useradd、sudo、sshd_config、chmod、SEL...
- 用
expect -d ./script.exp开启调试,看实际捕获到什么字符(注意:输出里可能含不可见控制符); - 用
-re支持正则,比如expect -re "(password:|yes/no|.*\$)"一次性覆盖多个可能提示; - 匹配字符串末尾带空格或制表符?加
.*更稳妥,如expect "password:.*"; - 远程 shell 提示符可能是
[root@host ~]#或user@host:~$,别硬写expect "$ ",用expect -re {[.*]|.*$}更可靠。
send 发送密码后脚本立刻退出,而不是继续执行命令
根本原因是缺少后续的 expect 等待点,send 只负责发,不负责等响应。发完密码就直接 send "ls
",但此时远程 shell 还没准备好接收命令,ls 就被丢弃了。
- 每次
send后,必须跟一个expect等待下一条提示(如expect "# "或expect -re {$|#}); - 不要省略
——send "ls "是回车,send "ls "在多数终端下无效; - 如果最后想留在交互状态,结尾加
interact;如果只想执行完就退出,结尾用expect eof; - 敏感信息别硬编码,用
set password $env(PASSWD)从环境变量读取,运行时PASSWD=12345 ./login.exp。
timeout 设为 -1 并不安全,真实场景要分层设值
设 set timeout -1 看似一劳永逸,但一旦远程主机宕机或网络中断,脚本会永远挂起,无法被信号中断,CI 流水线直接卡死。
- 连接阶段(spawn ssh)建议设 10~15 秒:
set timeout 15; - 密码输入后等待 shell 提示符,设 5~8 秒足够;
- 执行具体命令(如
send "df -h ")后,按命令预期耗时设,比如 rsync 设 120 秒,apt update设 300 秒; - 所有
expect块都应包含timeout分支,例如:expect { timeout { send_user "SSH connect failed "; exit 1 } "password:" { send "$passwd " } }。
真正难的从来不是语法,而是你得知道目标程序到底吐出了什么、什么时候吐、中间夹了几行无关日志、终端是否开了颜色转义——expect 不是魔法,它只忠实地做你告诉它做的那几件事。










