Eu não acho que você pode contornar isso.
Com -tt
, sshd
gera um pseudo-terminal e torna o escravo parte do stdin, stdout e stderr do shell que executa o comando remoto.
sshd
lê o que está vindo do seu (único) fd para a parte principal do pseudo-terminal e envia isso (via um único canal) para o ssh
cliente. Não existe um segundo canal para o stderr, pois existe sem -t
.
Além disso, observe que a disciplina da linha de terminal do pseudo-terminal pode (e por padrão) alterar a saída. Por exemplo, o LF será convertido em CRLF por lá e não no terminal local; portanto, convém desativar 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 ^C
personagem que causará um SIGINT, mas também outros sinais, o eco e todo o manuseio envolvido no editor de linha de modo canônico ).
Você poderia redirecionar o stderr para um 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 o uso -t
completo. Isso realmente significa apenas para uso interativo a partir de um terminal real.
Em vez de confiar na transmissão de um ^ C para permitir que a conexão remota seja encerrada, você pode usar um wrapper que faz a poll()
para detectar a ssh
conexão interrompida ou encerrada.
Talvez algo como (simplificado, você queira adicionar uma verificação de erro):
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)
exposto acima pode parecer bobagem, mas a idéia é aguardar um evento de interrupção (para que a extremidade de leitura do pipe no stdout seja fechada). POLLHUP como uma máscara solicitada é ignorada. POLLHUP é apenas significativo como um evento retornado (para dizer que o fim da gravação foi fechado).
Temos que fornecer um valor diferente de zero para a máscara de evento. Se usarmos 0
, perl
nem ligue poll
. Então, aqui usamos POLLIN.
No Linux, o que você solicitar, se o canal for quebrado, poll () retornará POLLERR.
No Solaris e no FreeBSD, onde os pipes são bidirecionais, quando a extremidade de leitura do pipe (que também é um fim de gravação) é fechada, ele retorna com POLLHUP (e POLLIN no FreeBSD, onde você precisa solicitar POLLIN ou $p->poll()
não Retorna).
Não posso dizer o quão portátil é fora desses três sistemas operacionais.
parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}
:, mas remova o '-tt' e, em seguida, ele não funciona.