Bash

“作業”顯示不再存在的程序正在執行

  • February 1, 2021

我在後台從 bash 執行一個長時間執行的管道:

find / -size +500M -name '*.txt' -mtime +90 |
  xargs -n1 gzip -v9 &

管道的第二階段需要很長時間才能完成(幾小時),因為有幾個大文件+舊文件。

相反,管道的第一部分立即完成,並且由於管道未滿,並且已經完成,因此find成功退出。

bash程序似乎wait適合子程序。

我可以告訴這一點,因為沒有find(pid 20851)根據以下任一方式執行:

    ps alx | grep 20851
    pgrep -l find

沒有殭屍程序,也沒有任何程序 ID20851可以在系統上的任何位置找到。

內置的 bashjobs正確地將作業顯示為一行,沒有任何程序 ID:

[1]+  Running find / -size +500M -name '*.txt' -mtime +90 | xargs -n1 gzip -v9 &

OTOH:我偶然發現了一個單獨的作業控制命令 ( /bin/jobs),它顯示:

[1]+ 20851 Running         find / -size +500M -name '*.txt' -mtime +90
    20852 Running         | xargs -n1 gzip -v9 &

並且(錯誤地)將已經退出的20851查找過程顯示為“正在執行”。

這是在 CentOS(編輯:更準確地說:Amazon Linux 2 AMI)Linux 上。原來這/bin/jobs是一個兩行/bin/sh腳本:

#!/bin/sh
builtin jobs "$@"

這讓我很驚訝。從另一個程序()開始的單獨程序如何sh知道由另一個(bash)管理的程序在該程序已經完成並退出並且不是殭屍之後的詳細資訊?

pid進一步:當系統上的其他方法(ps, pgrep)不能時,它如何知道有關已退出程序的詳細資訊(包括)?

編輯:

(1) 正如比利叔叔在評論中指出的那樣,在這個系統上/bin/sh/bin/bash是相同的(/bin/sh是一個符號連結/bin/bash),但是/bin/jobs是一個帶有 shebang 行的腳本,所以它在一個單獨的程序中執行。

(2) 另外,感謝比利叔叔:一種更簡單的複制方法。/bin/jobs是一條紅鯡魚。我錯誤地認為它是產生輸出的那個。jobs使用以下命令呼叫時,令人驚訝的輸出顯然來自內置 bash -l

$ sleep 1 | sleep 3600 &
[1] 13616
$ jobs -l
[1]+ 13615 Running                 sleep 1
    13616 Running                 | sleep 3600 &
$ ls /proc/13615
ls: cannot access /proc/13615: No such file or directory

因此程序 13615 不存在,但被 bash 內置作業控制顯示為“正在執行”,這看起來像jobs -l.

讓我覺得它一定是罪魁禍首(它不是)的存在/bin/jobs似乎令人困惑和可疑。我相信它應該從系統中刪除,因為它是無用的(sh在單獨的程序中執行的腳本,無論如何都不能顯示呼叫者的工作)。

FWIW,我可以通過以下方式重現您的案例:

rhel8$ /bin/jobs(){ jobs -l; }
rhel8$ sleep 1 | sleep 3600 &
[1] 2611
rhel8$ sleep 2
rhel8$ jobs
[1]+  Running                 sleep 1 | sleep 3600 &
rhel8$ /bin/jobs
[1]+  2610 Running                 sleep 1
     2611 Running                 | sleep 3600 &
rhel8$ pgrep 2610
   <nothing!>
rhel8$ ls /proc/2610
ls: cannot access '/proc/2610': No such file or directory
rhel8$ /bin/jobs
[1]+  2610 Running                 sleep 1
     2611 Running                 | sleep 3600 &
rhel8$ cat /bin/jobs
#!/bin/sh
builtin jobs "$@"

或與(甚至比以前更薄):

rhel8$ unset -f /bin/jobs
rhel8$ export JOBS=$(jobs -l)
rhel8$ builtin(){ echo "$JOBS"; }
rhel8$ export -f builtin
rhel8$ /bin/jobs
[1]+  2610 Running                 sleep 1
     2611 Running                 | sleep 3600 &
rhel8$ type /bin/jobs
/bin/jobs is /bin/jobs

注意:如前所述,jobs -l在 bash 中顯示的是陳舊資訊,已經退出的管道程序仍顯示為Running. 恕我直言,這是一個錯誤——zsh、ksh 或 yash 等其他 shell 正確地將它們顯示為Done.

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