You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Expect时HEREDOC与文件引用的差异及EOF异常问题

问题:Expect脚本通过HEREDOC运行时输入密码后触发EOF退出,文件运行正常

我写了一个用Expect通过SSH连接服务器的脚本,遇到奇怪的问题:

  • 用HEREDOC方式直接运行时,输入密码后会收到EOF并退出
  • 把完全相同的脚本保存为文件运行,却能正常获得交互shell

HEREDOC方式的代码片段

expect -d /dev/stdin <<- END
sleep 10
set timeout 20
eval spawn "ssh user@example.org"

expect "user@example.org's password: " { send -- "Passw0rd\r" }

interact
END

运行日志

expect: does "\ruser@example.org's password: " (spawn_id exp6) match glob pattern "rmh@user@example.org's password: "? yes
expect: set expect_out(0,string) "rmh@user@example.org's password: "
expect: set expect_out(spawn_id) "exp6"
expect: set expect_out(buffer) "\rrmh@user@example.org's password: "
send: sending "Passw0rd\r" to { exp6 }
interact: received eof from spawn_id exp0

文件方式的运行代码

cat <<- END > interact.expect
sleep 10
set timeout 20
eval spawn "ssh user@example.org"

expect "user@example.org's password: " { send -- "Passw0rd\r" }

interact
END

expect -d interact.expect

多次确认脚本内容完全一致,请问异常原因是什么?


原因分析

核心问题是HEREDOC模式下,Expect的标准输入被占用,导致interact无法获取终端输入:

  • 当用expect -d /dev/stdin <<- END时,Expect的标准输入是HEREDOC的内容流,当HEREDOC结束(遇到END),系统会给Expect的stdin发送EOF。
  • interact命令的作用是将用户的终端输入输出与spawn的SSH进程绑定,但此时Expect的stdin已经没有来自终端的输入(已经是EOF状态),所以interact会判定没有后续输入,触发EOF并终止SSH会话。
  • 而将脚本保存为文件运行时,Expect读取的是文件内容,它的stdin仍然指向当前终端,interact可以正常接管终端与SSH进程的交互,因此能保持会话正常。

解决办法

如果要通过HEREDOC运行,需要让Expect的stdin保持为终端,而非HEREDOC流,可改用expect -c参数传递脚本内容:

expect -d -c "$(cat <<- END
sleep 10
set timeout 20
eval spawn \"ssh user@example.org\"

expect \"user@example.org's password: \" { send -- \"Passw0rd\r\" }

interact
END
)"

或者直接用单引号包裹脚本内容传递给-c(注意转义单引号):

expect -d -c '
sleep 10
set timeout 20
eval spawn "ssh user@example.org"

expect "user@example.org'\''s password: " { send -- "Passw0rd\r" }

interact
'

内容的提问来源于stack exchange,提问作者45306

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 22:27:39