我正在运行以下命令,并监视其他系统上的输出文件:
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,您会发现它们默认是禁用的。