Expect脚本故障排查:登录服务器后无法运行本地脚本
你遇到的问题核心在于Expect的spawn命令不会像bash那样解析重定向符号<。当你直接在bash里执行ssh ... bash -s < ./emrScript.sh时,是本地bash把< ./emrScript.sh解析为“将本地脚本内容作为标准输入传给ssh命令”,远程的bash -s会从标准输入读取脚本内容并执行。
但在你的Expect脚本里,< ./emrScript.sh被当成了ssh命令的一个参数直接传给了远程服务器。远程服务器的bash收到这个参数后,会尝试在服务器本地查找./emrScript.sh文件——而这个文件只存在于你的本地机器,所以自然会报“找不到文件”的错误。
方案一:让本地shell处理重定向
把ssh命令放在本地bash的子shell里执行,这样重定向会被本地shell解析,而不是作为参数传给远程。修改你的spawn行如下:
spawn bash -c "ssh -o StrictHostKeyChecking=no $username@$sshHost 'bash -s' < ./emrScript.sh"
这样bash -c会启动一个本地bash进程,由它来处理< ./emrScript.sh的重定向,把本地脚本内容通过ssh的标准输入传给远程的bash -s,和你直接在终端执行ssh命令的逻辑完全一致。
方案二:手动读取本地脚本并发送给远程
如果你不想依赖本地bash,可以在Expect里先读取本地脚本的内容,然后把内容发送给远程的bash -s。修改后的脚本如下:
#!/usr/bin/expect set username [lindex $argv 0] set password [lindex $argv 1] set sshHost [lindex $argv 2] # 读取本地脚本内容 set script_file "./emrScript.sh" set fh [open $script_file r] set script_content [read $fh] close $fh # 启动ssh连接,只执行bash -s(等待输入脚本内容) spawn ssh -o StrictHostKeyChecking=no $username@$sshHost "bash -s" # 处理密码提示,发送密码后再发送脚本内容 expect "Enter your AD Password:" { send "$password\r" send "$script_content\r" expect eof } # 捕获退出码并返回 catch wait result exit [lindex $result 3]
这个方案的逻辑是:先连接远程服务器启动bash -s(它会等待标准输入的脚本内容),输入密码后,把本地脚本的内容全部发送给远程bash,让它执行。
你原来的Expect代码里,expect eof直接写在send后面的同一行,虽然语法没问题,但拆分到多行可读性更好,就像方案二里的写法一样。
内容的提问来源于stack exchange,提问作者KBouldin9

