ssh 终止后,ssh 命令意外地在其他系统上继续运行

ssh 终止后,ssh 命令意外地在其他系统上继续运行

我正在运行以下命令,并监视其他系统上的输出文件:

ssh $ip_address 'for n in 1 2 3 4 5; do sleep 10; echo $n >>/tmp/count; done'

^C如果我使用或仅终止我登录的终端来终止 ssh 命令,我希望远程命令也会终止。但这并没有发生:/tmp/count无论如何,都会获取所有数字 1-5,并ps -ejH显示 shell 及其sleep子进程继续运行。

这是预期的行为吗?它是否记录在任何地方?我可以禁用它吗?通过阅读,我预计必须使用 nohup 显式启用这种行为,而不是让它成为默认行为。

我浏览了 ssh 和 sshd 的手册页,但没有发现任何明显的东西,Google 向我指出了打开此行为的说明,而不是关闭它的说明。

我正在运行 Red Hat Enterprise Linux 6.2,两个系统上都使用 root 登录和 bash shell。

答案1

乌瑟尔的回答告诉您分配一个终端,但没有解释原因。原因并不是 ssh 特有的,而是信号生成和传播的问题。我邀请你阅读是什么原因导致发送各种信号?了解更多背景。

在远程主机上,有两个相关的进程:

  • ssh 守护进程 ( sshd) 的一个实例,它将远程程序的输入和输出中继到本地终端;
  • 一个正在运行该for循环的 shell。

shell 可以在到达循环末尾或遇到致命错误时自然死亡,也可以因信号而死亡。问题是,为什么 shell 会收到信号?

如果远程 shell 通过管道连接sshd(当您在命令行上指定命令时会发生这种情况)ssh,那么它将死于信号管道ifsshd退出并且 shell 尝试写入管道。只要 shell 不写入管道,它就不会收到 SIGPIPE。在这里,shell 永远不会向其标准输出写入任何内容,因此它可以永远存在。

您可以将该-t选项传递给 ssh,告诉它模拟远程端的终端并在此终端中运行指定的命令。然后,如果 SSH 客户端消失,则sshd关闭连接并退出,从而破坏终端。当终端设备消失时,在其中运行的任何进程都会收到叹息。因此,如果您终止 SSH 客户端(使用kill或通过关闭客户端正在运行的终端),远程 shell 会发出 SIGHUP 信号。

如果传递该-t选项,SSH 也会中继信号情报。如果按Ctrl+ C,则远程 shell 会收到 SIGINT。

答案2

尝试使用您的命令分配 psuedo-tty ssh

ssh -t $ip_address 'for n in 1 2 3 4 5; do sleep 10; echo $n >>/tmp/count; done'

当您断开 ssh 会话时,该进程应该终止。

由于这是一个伪 tty,因此您的 shell 初始化可能不会获取配置文件的源代码,从而使您的命令处于非常裸露的环境中。您可能需要设置一个.ssh/environment定义环境变量(例如 PATH)的文件。从man 1 ssh

Additionally, ssh reads ~/.ssh/environment, and adds lines of the format 
“VARNAME=value” to the environment if the file exists and users are allowed
to change their environment.  For more information, see the 
PermitUserEnvironment option in sshd_config(5).

答案3

-t作为使用选项ssh使远程命令在客户端退出或(被终止)时终止的替代方法ssh,可以使用命名管道在sshd关闭标准输入时将“EOF 转换为 SIGHUP”(请参阅Bug 396 - 未分配 pty 时 sshd 会成为孤儿进程)。

# sample code in Bash
# press ctrl-d for EOF
ssh localhost '
TUBE=/tmp/myfifo.fifo
rm -f "$TUBE"
mkfifo "$TUBE"
#exec 3<>"$TUBE"

<"$TUBE" sleep 100 &  appPID=$!

# cf. "OpenSSH and non-blocking mode", 
# http://lists.mindrot.org/pipermail/openssh-unix-dev/2005-July/023090.html
#cat >"$TUBE"
#socat -u STDIN "PIPE:$TUBE"
dd of="$TUBE" bs=1 2>/dev/null
#while IFS="" read -r -n 1 char; do printf '%s' "$char"; done > "$TUBE"

#kill -HUP -$appPID 
kill $appPID

rm -f "$TUBE"
'

答案4

如果你想禁用它(看似默认行为),你需要启用ssh 保持活动状态在客户端或服务器端。

如果您查看手册页中的 ssh-keep-alive-options,您会发现它们默认是禁用的。

相关内容