Eu não acho que você possa contornar isso.
Com -tt
, sshd
gera um pseudo-terminal e faz da parte escrava stdin, stdout e stderr do shell que executa o comando remoto.
sshd
lê o que está vindo de seu (único) fd para a parte principal do pseudo-terminal e envia (por meio de um único canal) para o cliente ssh
. Não há segundo canal para stderr, pois não há -t
.
Além disso, observe que a disciplina de linha terminal do pseudo-terminal pode (e por padrão) alterar a saída. Por exemplo, o LF será convertido em CRLF e não no terminal local, portanto você pode querer desabilitar o pós-processamento de saída.
$ 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
Muito mais coisas acontecerão no lado da entrada (como o caractere ^C
que causará um SIGINT, mas também outros sinais, o eco e todo o tratamento envolvido na linha modo canônico editor).
Você poderia redirecionar o stderr para o fifo e recuperá-lo usando um segundo ssh
:
ssh -tt host 'mkfifo fifo && cmd 2> fifo' &
ssh host 'cat fifo' >&2
Mas o melhor IMO seria evitar usar -t
no total. Isso é apenas para uso interativo de um terminal real.
Em vez de depender da transmissão de um ^ C para permitir que o controle remoto termine a conexão, você pode usar um wrapper que execute poll()
para detectar a conexão ssh
ou fechada encerrada.
Talvez algo como (simplificado, você deseje adicionar alguma verificação de erros):
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'
O $p->mask(STDOUT, POLLIN)
acima pode parecer bobo, mas a idéia é esperar por um evento de hang-hup (para o fim de leitura do canal no stdout ser fechado). POLLHUP como uma máscara solicitada é ignorada. O POLLHUP é apenas significativo como um evento retornado (para dizer que o fim escrito foi fechado).
Precisamos dar um valor diferente de zero para a máscara de evento. Se usarmos 0
, perl
nem mesmo chamará poll
. Então aqui nós usamos POLLIN.
No Linux, o que você requisitar, se o pipe for quebrado, poll () retorna POLLERR.
No Solaris e no FreeBSD, onde os pipes são bidirecionais, quando o final de leitura do pipe (que também é um fim de escrita) é fechado, ele retorna com POLLHUP (e POLLIN no FreeBSD, onde você precisa solicitar POLLIN ou então $p->poll()
não retorna).
Eu não posso dizer como é portável fora desses três sistemas operacionais.