これはSTDERRに出力を送信しますが、伝搬しません Ctrl+C (すなわち Ctrl+Cssh
を殺しますが、リモートsleep
は殺しません):
$ ssh localhost 'sleep 100;echo foo ">&2"'
これは伝播します Ctrl+C (すなわち Ctrl+Cssh
とリモートsleep
)を強制終了しますが、STDERRをSTDOUTに送信します。
$ ssh -tt localhost 'sleep 100;echo foo ">&2"'
2番目のSTDERR出力をSTDERRに送信するように強制しながら、それを伝搬するにはどうすればよいですか? Ctrl+C?
背景
GNU Parallelは「ssh -tt」を使用して伝播します Ctrl+C。これにより、リモートで実行中のジョブを強制終了できます。ただし、STDERRに送信されたデータは、受信側のSTDERRに送信され続ける必要があります。
あなたはそれを回避することはできないと思います。
_-tt
_を使用すると、sshd
は疑似端末を生成し、スレーブ部分をリモートコマンドを実行するシェルのstdin、stdout、およびstderrにします。
sshd
は、その(単一の)fdから擬似端末のマスター部分に送信されるものを読み取り、それを(単一のチャネルを介して)ssh
クライアントに送信します。 _-t
_がないため、stderrの2番目のチャネルはありません。
さらに、疑似端末の端末回線規則により、出力が変更される可能性があります(デフォルトでは変更されます)。たとえば、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
_
入力側ではさらに多くのことが起こります(SIGINTを引き起こす_^C
_文字など)だけでなく、他の信号、エコー、および標準モード行エディターに関連するすべての処理)。
Stderrをfifoにリダイレクトし、2番目のssh
を使用して取得することができます。
_ssh -tt Host 'mkfifo fifo && cmd 2> fifo' &
ssh Host 'cat fifo' >&2
_
しかし、最良の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'
_
上記の$p->mask(STDOUT, POLLIN)
はばかげているように見えるかもしれませんが、アイデアはhang-hupイベントを待つことです(標準出力のパイプの読み取り側が閉じられるため)。 requestedマスクとしてのPOLLHUPは無視されます。 POLLHUPは、返されるイベントとしてのみ意味があります(writingの終わりが閉じられたことを通知します)。
イベントマスクにはゼロ以外の値を指定する必要があります。 _0
_を使用する場合、Perl
はpoll
を呼び出すことさえありません。ここでは、POLLINを使用します。
Linuxでは、要求が何であれ、パイプが壊れた場合、poll()はPOLLERRを返します。
パイプが双方向であるSolarisおよびFreeBSDでは、パイプの読み取り側(書き込み側でもある)が閉じている場合、POLLHUP(およびFreeBSDではPOLLIN、POLLINまたはelse $p->poll()
は返されません)。
それ以外の点では、これら3つのオペレーティングシステムの外では、どれほどポータブルであるかはわかりません。
他のプラットフォームで機能させるには、これが最終的な解決策となりました。 sshクライアントが切断され、親がpid 1になったかどうかを確認します。
$SIG{CHLD} = sub { $done = 1; };
$pid = fork;
unless($pid) {
# Make own process group to be able to kill HUP it later
setpgrp;
exec $ENV{Shell}, "-c", ($bashfunc."@ARGV");
die "exec: $!\n";
}
do {
# Parent is not init (ppid=1), so sshd is alive
# Exponential sleep up to 1 sec
$s = $s < 1 ? 0.001 + $s * 1.03 : $s;
select(undef, undef, undef, $s);
} until ($done || getppid == 1);
# Kill HUP the process group if job not done
kill(SIGHUP, -${pid}) unless $done;
wait;
exit ($?&127 ? 128+($?&127) : 1+$?>>8)