ssh -t 上的标准错误

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。

Sté*_*las 5

我不认为你能解决这个问题。

使用-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 仅作为返回事件才有意义(告诉写入端已关闭)。

我们必须为事件掩码提供一个非零值。如果我们使用0perl甚至不调用poll。所以这里我们使用POLLIN。

在 Linux 上,无论您请求什么,如果管道损坏,poll() 都会返回 POLLERR。

在 Solaris 和 FreeBSD 上,管道是双向的,当管道的读取端(也是写入端)关闭时,它会返回 POLLHUP(以及 FreeBSD 上的 POLLIN,您必须请求 POLLIN 否则$p->poll()不会返回)。

除了这三个操作系统之外,我不能说它的便携性如何。