ssh -t上のstderr


11

これは出力をSTDERRに送信しますが、Ctrl+を伝搬しませんC(つまり、Ctrl+ Cはkillしますsshがリモートはしませんsleep)。

$ ssh localhost 'sleep 100;echo foo ">&2"'

これはCtrl+を伝播しますC(つまり、Ctrl+ Csshリモートsleepを強制終了します)が、STDERRをSTDOUTに送信します。

$ ssh -tt localhost 'sleep 100;echo foo ">&2"'

Ctrl+を伝播させながら、2番目に強制的にSTDERR出力をSTDERRに送信させるにはどうすればよいCですか?

バックグラウンド

GNU ParallelはCtrl+ を伝播するために「ssh -tt」を使用しますC。これにより、リモートで実行中のジョブを強制終了できます。ただし、STDERRに送信されたデータは、受信側のSTDERRに送信され続ける必要があります。

回答:


5

あなたはそれを回避することはできないと思います。

スポーン擬似端末とスレーブ部にリモートコマンドを実行するシェルのSTDIN、stdoutとstderrを行います。-ttsshd

sshdその(単一の)fdから擬似端末のマスター部分に送信されるものを読み取り、それを(単一のチャネルを介して)sshクライアントに送信します。がないのでstderrの2番目のチャネルはありません-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

入力側ではさらに多くのことが起こります(^CSIGINTを引き起こす文字だけでなく、その他の信号、エコー、および正規モードの行エディターに関連するすべての処理)。

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)上記の愚かに見えるかもしれませんが、考え方は(閉じられるように標準出力にパイプの読取終了のための)ハングHUPイベントを待つことです。要求されたマスクとしてのPOLLHUP は無視されます。POLLHUPは、返されたイベントとしてのみ意味があります(書き込みの終わりが閉じられたことを通知するため)。

イベントマスクにはゼロ以外の値を指定する必要があります。を使用する場合0perl呼び出しさえしませんpoll。したがって、ここではPOLLINを使用します。

Linuxでは、要求が何であれ、パイプが壊れた場合、poll()はPOLLERRを返します。

パイプの読み取り終了(も書き終わりですが)閉じているパイプは双方向でのSolarisとFreeBSD、、で、それはあなたがPOLLINを要求する必要がFreeBSD上でPOLLHUP(とPOLLIN、と返すか、他に$p->poll()ありません戻る)。

それ以外の点では、これら3つのオペレーティングシステムの外では、どれほどポータブルであるかはわかりません。


私はあなたの考えが好きですが、「-tt」が設定されていない限り、ラッパーに信号を検出させることはできません。これは機能しますparallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}が、「-tt」を削除すると機能しなくなります。
Ole

@OleTangeラッパーの目的は、sshが終了したときに(ssh接続のハングアップ時に)SIGHUPがリモートジョブに送信されるようにすることです。私はあなたのcatch_all_signalsが何をしているのか分かりませんが、SIGHUPであり、ssh接続がドロップされた後にのみ取得されます(そのため、標準出力に何かを出力すると、表示されません)。
ステファンChazelas

catch_all_signalsはすべてのシグナルをファイルに記録し、前述のように「-tt」で機能しますが、失敗すると失敗します。つまり、sshの終了時にcatch_wrapからSIGHUPを受け取りません。
Ole Tange 2014年

まだ-tt編集後にのみ機能します。コマンドを並列で実行しない場合、sshは実行元の端末を継承します。
Ole

@OleTange、私は再現できません、それは私にとってはうまくいきます、私が投稿したコードでそれをテストしましたか?catch_wrapとcatch_all_signalsをどこかに投稿してください。で-t、動作しないことを期待してます。
ステファンChazelas

1

他のプラットフォームで機能させるには、これが最終的な解決策となりました。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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.