使用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
相关产品推荐
相关产品推荐

