Bash

獲取管道後台程序PID的可靠方法

  • December 1, 2021

我需要檢索通過管道傳輸到另一個程序的程序的 PID,這些程序一起作為 bash 中的後台作業生成。以前我只是依靠pgrep,但事實證明在pgrep能夠找到該過程之前可能會有> 2s的延遲:

#!/bin/bash
cmd1 | cmd2 &
pid=$(pgrep cmd1) # emtpy in about 1/10

我發現針對這個問題的一些常見建議是使用程序替換而不是簡單的管道 ( cmd1 >(cmd2) & pid=$!) 或使用jobs內置函式。程序替換執行整個子shell(對於整個執行時),所以我現在寧願使用jobs,但我想避免兩次犯同樣的錯誤……

jobs如果我在生成它們後立即執行查找,我可以 100% 依賴於了解這兩個程序嗎?

#!/bin/bash
cmd1 | cmd2 &
pid=$(jobs -p %cmd1) # 10/10?

這可能是由於在後台執行作業,或者可能是一個怪癖set -x,但以下範例通常以任何順序列出執行的命令。到目前為止,輸出似乎是正確的,但我想完全排除在jobs作業開始之前jobs可以執行的可能性(或者至少不會列出兩個程序)!?jobs

#!/bin/bash
set -x
tail -f /dev/null | cat &
jobs -l
kill %tail

例子:

+ jobs -l
[1]+ 2802325 Running                 tail -f /dev/null
    2802326                       | cat &
+ tail -f /dev/null
+ kill %tail

同樣,在流程替換的情況下,我可以依靠pid=$!始終工作嗎?它到底是為了“擴展到最近執行的後台(非同步)命令的程序ID”而設計的?

當後台作業是表單的管道時cmd1 | cmd2,它仍然是單個後台作業。沒有辦法知道什麼時候cmd1開始。

每個&創建一個後台作業。一旦cmd &返回,shell 就會知道該後台作業:cmd & jobslists cmdcmd & pid=$!設置pid為執行的程序 ID cmd

管道cmd1 | cmd2創建了另外兩個子流程:一個要執行cmd1,一個要執行cmd2。這兩個程序都是執行後台作業的子程序的子程序。以下是程序樹的樣子bash -c '{ sleep 123 | sleep 456; } & jobs -p; sleep 789'

PID PPID CMD
268  265  |   \_ bash -c { sleep 123 | sleep 456; } & sleep 789
269  268  |       \_ bash -c { sleep 123 | sleep 456; } & sleep 789
270  269  |       |   \_ sleep 123
271  269  |       |   \_ sleep 456
272  268  |       \_ sleep 789

268 是原始的 bash 程序。269 是jobs -p列印的後台作業。270 和 271 是管道的左側和右側,它們都是後台作業 (269) 主程序的子程序。

我測試的 bash 版本(Linux 上為 5.0.17)優化了cmd1 | cmd2 &沒有大括號。在這種情況下,管道的左側與後台作業在同一程序中執行:

PID PPID CMD
392  389  |   \_ bash -c sleep 123 | sleep 456 & jobs -p; sleep 789
393  392  |       \_ sleep 123
394  392  |       \_ sleep 456
395  392  |       \_ sleep 789

您不能依靠這種行為在 bash 版本之間保持穩定,甚至可能跨平台、發行版、libc 版本等。

jobs -p %cmd1查找程式碼以 . 開頭的作業cmd1。它發現的是cmd1 | cmd2. jobs -p %?cmd2找到相同的工作¹。無法通過 bash 的內置功能訪問正在執行的程序 ID cmd1cmd2

如果您需要確定cmd1已經開始,請使用程序替換。

cmd1 >(cmd2)

你不知道什麼時候cmd2開始和結束。

如果您需要知道何時cmd1cmd2開始和結束,您需要讓它們同時工作,並讓它們通過命名管道進行通信。

tmp=$(mktemp -d) # Remove this in cleanup code
mkfifo "$tmp/pipe"
cmd1 >"$tmp/pipe" & pid1=$!
cmd2 <"$tmp/pipe" & pid2=$!
…

jobs命令在腳本中不是很有用。用於$!記住後台作業的 PID。

¹或者至少應該如此。我的版本抱怨一個模棱兩可的工作規範,這必須是一個錯誤,因為它說儘管只有一個工作。

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