Tôi không nghĩ bạn có thể vượt qua điều đó.
Với -tt
, sshd
sinh ra một thiết bị đầu cuối giả và làm cho phần nô lệ trở thành stdin, stdout và stderr của shell thực thi lệnh từ xa.
sshd
đọc những gì đến từ fd (đơn) của nó đến phần chính của thiết bị đầu cuối giả và gửi nó (thông qua một kênh) cho ssh
khách hàng. Không có kênh thứ hai cho stderr vì không có -t
.
Ngoài ra, lưu ý rằng kỷ luật dòng thiết bị đầu cuối của thiết bị đầu cuối giả có thể (và theo mặc định) sẽ thay đổi đầu ra. Chẳng hạn, LF sẽ được chuyển đổi thành CRLF ở đó chứ không phải trên thiết bị đầu cuối cục bộ, vì vậy bạn có thể muốn tắt xử lý hậu kỳ đầu ra.
$ 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
Nhiều thứ nữa sẽ xảy ra ở phía đầu vào (như ^C
ký tự sẽ gây ra SIGINT, nhưng cũng có các tín hiệu khác, tiếng vang và tất cả các xử lý liên quan đến trình chỉnh sửa dòng chế độ chính tắc ).
Bạn có thể có thể chuyển hướng stderr đến fifo và truy xuất nó bằng một giây ssh
:
ssh -tt host 'mkfifo fifo && cmd 2> fifo' &
ssh host 'cat fifo' >&2
Nhưng IMO tốt nhất sẽ là tránh sử dụng -t
hoàn toàn. Điều đó thực sự chỉ có nghĩa là để sử dụng tương tác từ một thiết bị đầu cuối thực sự.
Thay vì dựa vào việc truyền ^ C để cho kết thúc từ xa kết nối bị đóng, bạn có thể sử dụng trình bao bọc poll()
để phát hiện ssh
kết nối bị chết hoặc đóng.
Có thể một cái gì đó như (đơn giản hóa, bạn sẽ muốn thêm một số kiểm tra lỗi):
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'
Những điều $p->mask(STDOUT, POLLIN)
trên có vẻ ngớ ngẩn, nhưng ý tưởng là chờ đợi một sự kiện hang-hup (cho đầu đọc của ống trên thiết bị xuất chuẩn bị đóng). POLLHUP như một mặt nạ được yêu cầu bị bỏ qua. POLLHUP chỉ có ý nghĩa như một sự kiện được trả về (để nói rằng kết thúc bằng văn bản đã bị đóng).
Chúng ta phải đưa ra một giá trị khác không cho mặt nạ sự kiện. Nếu chúng ta sử dụng 0
, perl
thậm chí không gọi poll
. Vì vậy, ở đây chúng tôi sử dụng POLLIN.
Trên Linux, bất cứ điều gì bạn yêu cầu, nếu đường ống bị hỏng, poll () sẽ trả về POLLERR.
Trên Solaris và FreeBSD, nơi các đường ống là hai chiều, khi đầu đọc của ống (cũng là đầu viết ở đó) được đóng lại, nó sẽ trả về với POLLHUP (và POLLIN trên FreeBSD, trong đó bạn phải yêu cầu POLLIN hoặc nếu $p->poll()
không thì không yêu cầu POLLIN trở về).
Tôi không thể nói nó di động như thế nào ngoài ba hệ điều hành đó.
parallel --tag -j1 'ssh -tt localhost perl/catch_wrap perl/catch_all_signals & sleep 1; killall -{} ssh' ::: {1..31}
, nhưng loại bỏ '-tt' và sau đó nó không hoạt động.