ssh -t를 통한 stderr


11

이것은 출력을 STDERR로 보내지 만 Ctrl+를 전파하지는 않습니다 C(즉 Ctrl+ C는 kill ssh하지만 remote는하지 않습니다 sleep).

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

이것은 Ctrl+를 전파 하지만 C(즉, Ctrl+ C는 kill ssh및 remote sleep) STDERR을 STDOUT으로 보냅니다.

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

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

배경

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

답변:


5

나는 당신이 그 문제를 해결할 수 있다고 생각하지 않습니다.

함께 -tt, sshd스폰 의사 단말 및 슬레이브 부분을 원격 명령을 실행 쉘의 표준 입력, 표준 출력과 표준 에러를 만든다.

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

그러나 가장 좋은 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)위 의 내용은 어리석은 것처럼 보이지만 행 아웃 이벤트 (stdout에서 파이프의 읽기 끝이 닫힐 때까지)를 기다리는 것이 좋습니다. 요청 된 마스크 인 POLLHUP 은 무시됩니다. POLLHUP은 반환 이벤트로만 의미가 있습니다 ( 쓰기 끝이 닫 혔음을 나타냅니다 ).

이벤트 마스크에 0이 아닌 값을 제공해야합니다. 우리가 사용하는 경우 0, perl전화도하지 않습니다 poll. 여기서는 POLLIN을 사용합니다.

Linux에서 요청한 내용에 관계없이 파이프가 손상되면 poll ()은 POLLERR을 반환합니다.

파이프의 읽기 끝 (또한 쓰기 끝이) 폐쇄 파이프 양방향 솔라리스와 FreeBSD,에, 당신이 POLLIN을 요청해야 FreeBSD의에 POLLHUP (그리고 POLLIN로 반환 그렇지 않으면 $p->poll()하지 않습니다 반환).

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


나는 당신의 아이디어를 좋아하지만 '-tt'가 설정되어 있지 않으면 래퍼가 신호를 감지하도록 할 수 없습니다. 이것은 작동 parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}하지만 '-tt'를 제거하면 작동하지 않습니다.
Ole Tange

@OleTange 래퍼의 목적은 ssh가 종료 될 때 (ssh 연결 끊기시) SIGHUP이 원격 작업으로 전송되는 것입니다. catch_all_signals의 기능을 모르지만 SIGHUP 만 있으면 ssh 연결이 끊긴 후에 만 ​​가능합니다 (따라서 stdout에 아무것도 인쇄하면 표시되지 않습니다).
Stéphane Chazelas

catch_all_signals는 모든 신호를 파일에 기록하며 언급 한 것처럼 '-tt'와 함께 작동하지만 실패합니다. 즉, ssh가 죽으면 catch_wrap으로부터 SIGHUP을받지 않습니다.
Ole Tange

여전히 -tt편집 후에 만 작동 합니다. 병렬을 통해 명령을 실행하지 않으면 ssh는 명령을 실행하는 터미널을 상속합니다.
Ole Tange

@OleTange, 재현 할 수 없습니다, 저에게 효과적입니다. 내가 게시 한 코드로 테스트 했습니까? 내가 볼 수 있도록 catch_wrap 및 catch_all_signals를 어딘가에 게시하십시오. 와 함께 작동 하지 않을-t 것으로 기대합니다 .
Stéphane Chazelas

1

다른 플랫폼에서 작동하게하려면 이것이 최종 솔루션이되었습니다. 그것은 ssh 클라이언트의 연결이 끊어졌고 부모가 pid가되었는지 확인합니다.

$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.