ssh -t를 통한 stderr

ssh -t를 통한 stderr

그러면 출력이 STDERR로 전송되지만 Ctrl+는 전파되지 않습니다 C(즉, Ctrl+는 C종료되지만 ssh원격은 종료되지 않음 sleep).

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

이는 Ctrl+를 전파하지만 C(즉, Ctrl+는 원격 C을 종료합니다 ) STDERR을 STDOUT으로 보냅니다.sshsleep

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

Ctrl+를 계속 전파하면서 두 번째 출력이 STDERR 출력을 STDERR로 보내도록 하려면 어떻게 해야 합니까 C?

배경

CtrlGNU Parallel은 "ssh -tt"를 사용하여 + 를 전파합니다 C. 이를 통해 원격으로 실행 중인 작업을 종료할 수 있습니다. 그러나 STDERR로 전송된 데이터는 수신 측에서 계속 STDERR로 이동해야 합니다.

답변1

나는 당신이 이 문제를 해결할 수 없다고 생각합니다.

-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

입력 측에서 훨씬 더 많은 일이 발생합니다(예: ^CSIGINT를 발생시키는 문자뿐만 아니라 기타 신호, 에코 및 관련된 모든 처리도 포함).정식 모드라인 편집기).

stderr를 fifo로 리디렉션하고 두 번째 것을 사용하여 검색할 수 있습니다 ssh.

ssh -tt host 'mkfifo fifo && cmd 2> fifo' &
ssh host 'cat fifo' >&2

하지만 제 생각에는 가장 좋은 방법은 -t그것을 완전히 피하는 것입니다. 이는 실제 터미널에서 대화식으로 사용하는 경우에만 적합합니다.

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)어리석게 보일 수 있지만 보류 중인 이벤트(stdout에서 파이프의 읽기 끝이 닫힐 때까지)를 기다리는 것이 아이디어입니다. POLLHUP으로필수의마스크는 무시됩니다. POLLHUP은 반환 이벤트로만 의미가 있습니다.글쓰기끝은 닫혀 있습니다).

이벤트 마스크에 0이 아닌 값을 지정해야 합니다. 을 사용하면 0호출 perl되지도 않습니다 poll. 그래서 여기서는 POLLIN을 사용합니다.

Linux에서는 요청한 내용에 관계없이 파이프가 끊어지면 poll()이 POLLERR을 반환합니다.

Solaris 및 FreeBSD에서 파이프는 양방향이며 파이프의 읽기 끝(또한 쓰기 끝)이 닫히면 POLLHUP으로 반환됩니다. FreeBSD에서는 POLLIN을 요청해야 하며 그렇지 않으면 $p->poll()반환되지 않습니다. ) 반품).

이 세 가지 운영 체제 외부에서 얼마나 이식성이 뛰어난지는 말할 수 없습니다.

답변2

다른 플랫폼에서도 작동하도록 하기 위한 최종 솔루션이 되었습니다. 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)

관련 정보