預測之前啟動的 SSH 命令的 PID
這是最奇怪的事情。
在腳本中,我啟動了一個 SSH 隧道,如下所示:
ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar
這將啟動一個
ssh
進入後台的實例,並繼續執行腳本。接下來,我使用 bash 的$!保存它的 PID(以便稍後殺死它)變數。為此,我附加&
到ssh
命令,即使它已經自行進入後台(否則$!
不包含任何內容)。因此,例如以下腳本:#!/bin/bash ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar & echo $! pgrep -f "ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar"
輸出
(some ssh output) 28062 28062
…正如預期的那樣,兩倍的 PID。但是,現在,當我從終端執行這個確切的命令序列時
$!
,輸出的 PID是錯誤的(從某種意義上說,它不是ssh
實例的 PID )。從終端:$ ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar & [1] 28178 (some ssh output) $ echo $! 28178 $ pgrep -f "ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar" 28181
也不總是相隔 3 個數字。我還觀察到了 1 或 2 的差異。但它絕不是相同的 PID,正如我所期望的那樣,並且在腳本中執行此命令序列時確實如此。
- 有人可以解釋為什麼會這樣嗎?我認為這可能是由於最初的
ssh
呼叫實際上分叉了另一個程序,但是為什麼它可以在腳本中工作呢?- 這也讓我懷疑
$!
在我的腳本中使用ssh
如上所述的 PID 是否確實總是有效(儘管到目前為止)。這真的可靠嗎?我覺得它比使用pgrep
… “更清潔”
shell的
$!
變數只知道shell啟動的程序的pid。正如您所懷疑的,ssh
使用-f
fork 自己的程序的呼叫,因此它可以進入後台,所以整個程序樹看起來像$$ 1 $$:
shell | +--ssh<1> (pid is $!) | +--ssh<2> (pid is different)
ssh<1>
呼叫後很快退出;因此, in 的值$!
不太可能有用。它ssh<2>
正在為您進行遠端通信並進行隧道傳輸,而可靠地獲取其 PID 的唯一方法是檢查程序表,就像您正在做的那樣pgrep
$$ 2 $$. 該
pgrep
方法可能是正確的方法。 至於為什麼它在腳本中起作用但不能互動,這可能是一種競爭條件。因為你把第一個ssh
放在後台,shell 和ssh
並發執行,並ssh
執行一些中等 CPU 密集型的加密身份驗證和一些網路往返。pgrep
您在腳本中執行的可能只是在ssh<1>
分叉自身進入後台之前執行。要解決此問題,請pgrep
稍後執行,或者通過sleep
呼叫,或者僅在稍後實際需要 PID 時呼叫它。$$ 1 $$
ssh
: 從技術上講,如果使用經典的雙叉作為背景,它可能比這更複雜。ssh
在這種情況下,兩者之間會有另一個短暫的過程。 $$ 2 $$: 除非你systemd
正在使用 cgroups 或其他東西來跟踪你所有的孩子。你不是。