
看起來可能有問題openssh
。在bash
shell 中,如果我重定向stderr
到stdout
它,它就會永遠阻塞,所以我必須KeyboardInterrupt
:
$ ssh -fTNF './config' -MS './sockd5/ctrl_socket' -i './keys/id_rsa' -l 'root' -p '22' 'example.com' 2>&1 | cat -A; echo OK
ControlSocket ./sockd5/ctrl_socket already exists, disabling multiplexing^M$
^C
沒有重定向的相同命令可以正常工作:
$ ssh -fTNF './config' -MS './sockd/ctrl_socket' -i './keys/id_rsa' -l 'root' -p '22' 'example.com' | cat -A; echo OK
ControlSocket ./sockd/ctrl_socket already exists, disabling multiplexing
OK
為什麼會發生這種情況?有解決方法嗎?
答案1
當ssh -f
連接並驗證到主機後「進入背景」時,它將繼續保留其原始 stdin、stdout 和 stderr 的開啟句柄,因此如果這些句柄透過管道連接到其他進程(如其 stdout + stderr在您的範例中cat -A
),它將具有使這些進程保持活動狀態的效果,即使它們不再需要。
ssh
透過呼叫來守護程式自身daemon(3)
函式庫函數,但它使用 來呼叫它noclose = 1
,從而阻止它從 重定向 stdin/stderr/stdout /dev/null
。
這在最新版本中已部分修復openssh
(對於主控制進程——標準輸入和標準輸出2010年,標準錯誤2016年;對於會話進程-標準輸出2017年),但如果您必須運行較舊的 ssh,或者需要它也停止堅持 stderr,唯一的“解決方案”可能是使用 hack LD_PRELOAD
,用一個包裝器覆蓋該daemon(3)
函數,該包裝器用noclose = 0
.
$ cat daemon-force-close.c
#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
#include <err.h>
int daemon(int nochdir, int noclose){
static int (*orig)(int, int);
if(!orig && !(*(void**)&orig = dlsym(RTLD_NEXT, "daemon")))
errx(1, "%s", dlerror());
return orig(nochdir, 0);
}
$ cc -shared -Wall -O2 daemon-force-close.c -ldl -o daemon-force-close.so
$ LD_PRELOAD=./daemon-force-close.so \
ssh -Nf dummy@localhost -MS ./ctlsock 2>&1 | cat -A
dummy@localhost's password:
$
[no Ctrl-C needed]
$ ssh -S ~/w/c/ctlsock dummy@localhost
Last login: Tue May 28 21:04:26 2019 from ::1
...
答案2
為什麼會發生這種情況?
cat
在 shell 到達 之前需要終止它echo
。它“永遠阻塞”,因為cat
生命。
有解決方法嗎?
在 Bash 中,我會使用進程替換來運行cat
,不會阻塞。有了cat
out of the way,echo OK
只有當它確實沒問題時(使用&&
或$?
)才很容易。例子:
ssh -f … > >(cat -A) 2>&1 && echo OK; echo "The script goes on."
現在在進入背景cat
之前和之後都可以工作ssh
,但是腳本一旦ssh
執行就會繼續(或一旦ssh
失敗而不進入背景)。