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

Terminating SSH session executed by bash script

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

1
2
3
4
5
#!/bin/bash
ssh user@host.com <<EOF
  nohup /path/to/run.sh &
EOF

echo 'done'

在运行nohup之后,它挂起。我必须按ctrl-c退出脚本。

我尝试在here文档的末尾添加一个显式的出口,并对ssh使用"-t"参数。两者都不起作用。如何使此脚本立即退出?

编辑:客户端是OSX 10.6,服务器是Ubuntu。


我认为问题在于,当您从ssh进入时,nohup不能重定向输出,只有当它认为nohup.out连接到终端时,它才会重定向到nohup.out,并且我,即使使用-t,您使用的stdin覆盖也会阻止这种情况。

解决方法可能是自己重定向输出,然后ssh客户机可以断开连接-它不等待流关闭。类似:

1
nohup /path/to/run.sh > run.log &

(在一个从OSX客户机连接到Ubuntu服务器的简单测试中,这对我很有用。)


问题可能是…

1
2
... ssh is respecting the POSIX standard when not closing the session
if a process is still attached to the tty.

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

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

请参见:使用nohup时ssh挂起exit

另一种方法可能是使用ssh -t -t强制进行伪tty分配,即使stdin不是终端。

1
2
3
4
5
man ssh | less -Ip 'multiple -t'

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

请参见:bash为ssh生成子shell并继续程序流


当调用ssh而不使用显式命令时,从here文档重定向远程主机的stdin会导致消息:Pseudo-terminal will not be allocated because stdin is not a terminal.

为避免出现此消息,请使用ssh-t开关告知远程主机不需要分配伪终端,或者显式指定一个命令(如/bin/sh命令)让远程主机执行本文提供的命令。

如果向ssh发出一个显式命令,则默认情况下不提供伪终端形式的登录shell,即在指定命令时不会有正常的登录会话(参见man ssh)。

另一方面,如果没有为ssh指定的命令,则默认情况是为远程主机上的交互式登录会话创建一个伪tty。

1
2
3
- ssh user@host.com <<EOF
+ ssh -T user@host.com <<EOF
+ ssh user@host.com /bin/bash <<EOF

通常情况下,只有当有命令期望stdin/stdout是终端(如topvim时,或者当ssh客户机命令完成执行时,有必要杀死远程shell及其子级时,才应使用ssh -t或甚至ssh -t -t(参见:ssh命令在其他服务器上意外地继续运行ssh终止后的系统)。

据我所知,将不分配伪tty的ssh命令和写入远程主机上nohup.outnohup命令组合在一起的唯一方法是让nohup命令在不由ssh机制创建的伪终端中执行。例如,这可以通过script命令来完成,并且可以避免tcgetattr: Inappropriate ioctl for device消息。

1
2
3
4
5
6
7
8
9
10
#!/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

最后需要添加一个exit 0