Я не думаю, что вы можете обойти это.
С -tt, sshdпорождает псевдо-терминал и делает ведомый участию 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
На входной стороне произойдет намного больше вещей (например, ^Cсимвол, который вызовет SIGINT, но также и другие сигналы, эхо и вся обработка, задействованная в редакторе строк канонического режима ).
Вы можете перенаправить stderr в fifo и получить его, используя секунду ssh:
ssh -tt host 'mkfifo fifo && cmd 2> fifo' &
ssh host 'cat fifo' >&2
Но лучше всего ИМО будет избегать использования в -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'
$p->mask(STDOUT, POLLIN)Выше может показаться глупым, но идея состоит в том, чтобы ждать , пока событие похмелья HUP (на конец чтения трубы на стандартный вывод должен быть закрыт). POLLHUP в качестве запрошенной маски игнорируется. POLLHUP имеет смысл только как возвращаемое событие (чтобы сказать, что конец записи был закрыт).
Мы должны дать ненулевое значение для маски события. Если мы используем 0, perlдаже не звонить poll. Так что здесь мы используем Поллин.
В Linux, что бы вы ни запросили, если канал разорван, poll () возвращает POLLERR.
В Solaris и FreeBSD, где каналы являются двунаправленными, когда конец чтения канала (который также является концом записи там) закрыт, он возвращается с POLLHUP (и POLLIN во FreeBSD, где вы должны запросить POLLIN, иначе $p->poll()нет возвращение).
Я не могу сказать, насколько он переносим за пределами этих трех операционных систем.
parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}но удалите '-tt' и тогда это не будет работать.