Ole*_*nge 11 shell ssh signals file-descriptors stderr
这会将输出发送到 STDERR,但不会传播Ctrl+ C(即Ctrl+C会杀死ssh但不会杀死远程sleep):
$ ssh localhost 'sleep 100;echo foo ">&2"'
Run Code Online (Sandbox Code Playgroud)
这传播Ctrl+ C(即Ctrl+C将杀死ssh和远程sleep),但将 STDERR 发送到 STDOUT:
$ ssh -tt localhost 'sleep 100;echo foo ">&2"'
Run Code Online (Sandbox Code Playgroud)
如何强制第二个将 STDERR 输出发送到 STDERR,同时仍在传播Ctrl+ C?
背景
GNU Parallel 使用 'ssh -tt' 来传播Ctrl+ C。这使得终止远程运行的作业成为可能。但是发送到 STDERR 的数据在接收端应该继续去 STDERR。
我不认为你能解决这个问题。
使用-tt,sshd产生一个伪终端并使从属部分成为执行远程命令的 shell 的 stdin、stdout 和 stderr。
sshd将来自其(单个)fd 的内容读取到伪终端的主部分,并将其(通过一个单一通道)发送到ssh客户端。stderr 没有第二个通道,因为没有-t.
此外请注意,伪终端的终端线路规则可能(并且默认情况下)会改变输出。例如,LF 将在那里转换为 CRLF,而不是在本地终端上,因此您可能希望禁用输出后处理。
$ 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
Run Code Online (Sandbox Code Playgroud)
在输入端会发生更多的事情(比如^C会导致 SIGINT的字符,还有其他信号、回声和规范模式行编辑器中涉及的所有处理)。
您可以将 stderr 重定向到 fifo 并使用第二个检索它ssh:
ssh -tt host 'mkfifo fifo && cmd 2> fifo' &
ssh host 'cat fifo' >&2
Run Code Online (Sandbox Code Playgroud)
但最好的 IMO 是-t完全避免使用。这实际上仅适用于从真实终端进行交互使用。
与其依赖 ^C 的传输来让远程端关闭连接,您还可以使用一个包装器poll()来检测被终止ssh或关闭的连接。
也许类似(简化,您需要添加一些错误检查):
LC_HUP_DETECTOR='
use IO::Poll;
$SIG{CHLD} = sub {$done = 1};
$p = IO::Poll->new;
$p->mask(STDOUT, POLLIN);
$pid=fork; unless($pid) {setpgrp; exec @ARGV; die "exec: $!\n"}
$p->poll;
kill SIGHUP, -$pid unless $done;
wait; exit ($?&127 ? 128+($?&127) : 1+$?>>8)
' ssh host 'perl -e "$LC_HUP_DETECTOR" some cmd'
Run Code Online (Sandbox Code Playgroud)
在$p->mask(STDOUT, POLLIN)上述可能看起来很傻,但这个想法是等待挂起HUP事件(在标准输出管道的读出端被关闭)。POLLHUP 作为请求的掩码被忽略。POLLHUP 仅作为返回事件才有意义(告诉写入端已关闭)。
我们必须为事件掩码提供一个非零值。如果我们使用0,perl甚至不调用poll。所以这里我们使用POLLIN。
在 Linux 上,无论您请求什么,如果管道损坏,poll() 都会返回 POLLERR。
在 Solaris 和 FreeBSD 上,管道是双向的,当管道的读取端(也是写入端)关闭时,它会返回 POLLHUP(以及 FreeBSD 上的 POLLIN,您必须请求 POLLIN 否则$p->poll()不会返回)。
除了这三个操作系统之外,我不能说它的便携性如何。