Bash

預測之前啟動的 SSH 命令的 PID

  • December 18, 2015

這是最奇怪的事情。

在腳本中,我啟動了一個 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,正如我所期望的那樣,並且在腳本中執行此命令序列時確實如此。

  1. 有人可以解釋為什麼會這樣嗎?我認為這可能是由於最初的ssh呼叫實際上分叉了另一個程序,但是為什麼它可以在腳本中工作呢?
  2. 這也讓我懷疑$!在我的腳本中使用ssh如上所述的 PID 是否確實總是有效(儘管到目前為止)。這真的可靠嗎?我覺得它比使用pgrep… “更清潔”

shell的$!變數只知道shell啟動的程序的pid。正如您所懷疑的,ssh使用-ffork 自己的程序的呼叫,因此它可以進入後台,所以整個程序樹看起來像

$$ 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 或其他東西來跟踪你所有的孩子。你不是。

引用自:https://unix.stackexchange.com/questions/230615