由 bash 脚本执行的 SSH 会话终止

21

我有一个脚本可以在本地远程启动服务器:

#!/bin/bash
ssh user@host.com <<EOF
  nohup /path/to/run.sh &
EOF
echo 'done'

在运行nohup之后,它会挂起。我必须按ctrl-c来退出脚本。
我尝试在此处文档的结尾添加明确的退出,并使用ssh的"-t"参数。但两者都无效。我该如何使此脚本立即退出?
编辑:客户端是OSX 10.6,服务器是Ubuntu。

2
你使用的是哪个SSH客户端?除非客户端做了一些奇怪的事情,否则-t选项应该可以工作。 - Ben.Vineyard
抱歉,我应该提到这是在OSX上,使用标准SSH客户端。已更新问题。 - mahemoff
EOF 的意思是什么? - Green Lei
1
@GreenLei "文件结束",但这是一个HereDoc,因此可以使用任何其他单词,例如END。只要两个地方都相同即可。 - mahemoff
4个回答

18
我认为问题在于当您通过ssh登录时,nohup无法重定向输出,它只有在认为自己连接到终端时才会将输出重定向到nohup.out,而您使用的stdin覆盖将阻止此操作,即使使用-t选项也是如此。
一种解决方法可能是自行重定向输出,然后ssh客户端就可以断开连接,不必等待流关闭。类似以下命令:
```bash command > output.log 2>&1 & ```
nohup /path/to/run.sh > run.log &

(这个方法在我连接从OS X客户端连接到Ubuntu服务器的简单测试中有效。)

太好了,谢谢。我之前尝试过将输入重定向到/dev/null,但没想到还可以尝试输出!(我已经测试过将输出重定向到/dev/null也可以正常工作,即 nohup /path/to/run.sh > /dev/null &) - mahemoff
我遇到了与OP类似的问题。我正在从一个csh脚本中进行ssh连接,运行远程命令,并尝试将输出发送回csh脚本中的变量。您认为这是可能的吗?我遇到了相同的错误:我正在执行ssh -t -t并收到错误消息:Inappropriate IOCtl for device - krb686

8
问题可能是...
... ssh is respecting the POSIX standard when not closing the session 
if a process is still attached to the tty.

因此,解决方案可能是将 nohup 命令的 stdin 与 tty 分离:

nohup /path/to/run.sh </dev/null &

参考:当使用nohup时,SSH退出卡住的问题

另一种方法是使用ssh -t -t 强制分配虚拟终端(即使 stdin 不是终端)。

man ssh | less -Ip 'multiple -t'

ssh -t -t user@host.com <<EOF
  nohup /path/to/run.sh &
EOF

请看:BASH出现SSH的子壳并继续程序流

第一个没有起作用,结果和之前一样。-t -t 确实 起作用了 (-t -t),但是出现了“tcgetattr: Inappropriate ioctl for device”错误,考虑到双重 -t 的作用,这是有道理的。我搜索了一下如何消除这个错误(我宁愿尽可能地干净利落地解决),但是在 heredoc 中找不到解决方法。结果发现 Martin Clayton 的答案解决了这个问题,所以我就把它留在那里了。 - mahemoff

2
将远程主机的标准输入从一个here文档重定向到ssh而没有显式命令会导致以下消息:Pseudo-terminal will not be allocated because stdin is not a terminal. 为了避免这个消息,可以使用ssh-T开关告诉远程主机不需要分配伪终端,或者明确指定一个命令(如/bin/sh)让远程主机执行这里文档提供的命令。如果给ssh指定了一个明确的命令,则默认情况下不会提供伪终端形式的登录shell,即在指定了命令时不会有正常的登录会话(参见man ssh)。另一方面,如果没有为ssh指定命令,则默认情况下会为远程主机创建一个伪tty,用于交互式登录会话。
- ssh user@host.com <<EOF
+ ssh -T user@host.com <<EOF
+ ssh user@host.com /bin/bash <<EOF

通常情况下,只有当有命令需要标准输入/输出为终端(如topvim)或者需要在ssh客户端命令执行完毕后杀死远程shell及其子进程时(参见:ssh command unexpectedly continues on other system after ssh terminates),才应该使用ssh -tssh -t -t
据我所知,结合一个不分配伪终端的ssh命令和一个写入到远程主机的nohup.outnohup命令的唯一方法是让nohup命令在ssh机制未创建的伪终端中执行。例如,可以使用script命令来实现这一点,并避免tcgetattr:Inappropriate ioctl for device消息的出现。
#!/bin/bash
ssh localhost /bin/sh <<EOF
  #0<&-  script -q /dev/null nohup sleep 10 1>&- &
  #0<&- script -q -c "nohup sh -c 'date; sleep 10 1>&- &'" /dev/null  # Linux
  0<&- script -q /dev/null nohup sh -c 'date; sleep 10 1>&- &'        # FreeBSD, Mac OS X
  cat nohup.out
  exit 0
EOF
echo 'done'
exit 0

1
你需要在结尾处添加一个 exit 0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接