我只是注意到,如果我执行ssh user@remote_host tail -f /some/file
,那么tail -f /some/file
即使 ssh 连接关闭,也会在remote_host上继续运行!
因此,在多次连接和断开连接之后,运行次数tail -f /some/file
会增加。tail -f
当 ssh 连接关闭时如何实际终止?
答案1
在
ssh host tail -f file
客户端通过 TCP 连接ssh
连接到sshd
服务器。运行时其标准输出重定向到管道。读取来自管道另一端的内容并将其封装在 sshd 协议中以发送到客户端。 (使用 时,stdout 会直接是套接字,但增加了加密,并且能够在单个 TCP 连接上复用多个流(例如端口/代理/X11/隧道重定向、stderr),因此必须求助于管道)。host
sshd
tail -f
sshd
ssh
rshd
tail
sshd
当您按 CTRL-C 时,SIGINT 会发送到ssh
客户端。那会导致ssh
死亡。一旦死亡,TCP 连接就会关闭。因此, onhost
也sshd
死亡。tail
没有被杀死,但它的标准输出现在是一个管道,另一端没有读取器。因此,下次它向其标准输出写入内容时,它将收到 SIGPIPE 并死亡(尽管最新版本的 GNUtail
监视器会发现其标准输出变成损坏的管道并在这种情况下立即退出,另请参阅这个相关问题的答案了解如何检测破损的管道)。
在:
ssh -t host 'tail -f file'
两者是相同的,只是 和 之间的通信不是通过管道,sshd
而是tail
通过伪终端进行。tail
的 stdout 是一个从属伪终端(如/dev/pts/12
),并且主端上tail
的任何写入(可能由 tty 线路规则修改)并封装发送到客户端。read
sshd
ssh
在客户端,使用-t
,ssh
将终端置于raw
模式。特别是,这会禁用终端规范模式和终端信号处理。
因此,当您按 时Ctrl+C,客户端的终端线路规则不会向作业发送 SIGINT ssh
,而是^C
通过连接将字符发送到远程终端的主端sshd
并将sshd
其写入。^C
远程终端的线路纪律发送一个SIGINT
to tail
。tail
然后死亡,sshd
退出并关闭连接并ssh
终止(如果它不是仍然忙于端口转发或其他)。
另外,使用 时-t
,如果ssh
客户端终止(例如,如果您输入~.
),连接将关闭并sshd
终止。结果,一个 SIGHUP 将被发送到tail
.
现在,请注意使用-t
有副作用。例如,使用默认终端设置,\n
字符会被转换为\r\n
,并且可能会发生更多情况,具体取决于远程系统,因此stty -opost
如果该输出不适合在远程主机上发出(以禁用输出后处理)一个终端:
$ ssh localhost 'echo x' | hd
00000000 78 0a |x.|
00000002
$ ssh -t localhost 'echo x' | hd
00000000 78 0d 0a |x..|
00000003
$ ssh -t localhost 'stty -opost; echo x' | hd
00000000 78 0a |x.|
00000002
-t
使用/的另一个缺点-tt
是 stdout 和 stderr 在客户端上不区分。远程命令的 stdout 和 stderr 都将写入ssh
客户端的 stdout:
$ ssh localhost ls /x | wc -l
ls: cannot access /x: No such file or directory
0
$ ssh -t localhost ls /x | wc -l
1
答案2
您需要在远程端分配终端:
ssh -t user@remote_host tail -f /some/file
甚至
ssh -tt user@remote_host tail -f /some/file