【问题标题】:How to kill all subprocesses of shell?如何杀死shell的所有子进程?
【发布时间】:2011-02-06 18:51:03
【问题描述】:

我正在编写一个 bash 脚本,它做了几件事。

一开始它会启动几个监控脚本,每个脚本都运行一些其他工具。

在我的主脚本结束时,我想杀死所有从我的 shell 中生成的东西。

所以,它可能看起来像这样:

#!/bin/bash

some_monitor1.sh &
some_monitor2.sh &
some_monitor3.sh &

do_some_work
...

kill_subprocesses

问题是这些监视器中的大多数都会产生自己的子进程,所以这样做(例如):killall some_monitor1.sh 并不总是有帮助。

有没有其他办法处理这种情况?

【问题讨论】:

标签: bash shell process fork kill


【解决方案1】:
pkill -P $$

适合(只是杀死它自己的后代)

编辑:我投了反对票,不知道为什么。无论如何,这里是-P的帮助

   -P, --parent ppid,...
          Only match processes whose parent process ID is listed.

$$process id of the script itself

【讨论】:

  • 不杀死通过直接子进程的后代。
  • @Chexpir:它不是 bash 的一部分。但是,如果您的系统没有它,那么您就处于 deep5h1t 中。它是 Ubuntu 下 procps 软件包的一部分,该软件包包含其他核心实用程序,例如 freekillps
  • 赞成。然而在我的情况下,这些过程非常顽固,所以我不得不用信号 9 杀死它们,就像pkill -P $$ --signal 9
  • @pihentagy 没有竞争条件吗? pkill 本身是当前 shell 的子级。因此,“pkill -P $$”会在所有其他进程被杀死之前杀死“pkill”进程吗?
  • 不,这里没有竞争条件。 pkill 是 pgrep 的符号链接,在 pgrep.c 的第 441 行中,您可以看到 pkill 在检查要杀死的进程时会跳过自身。因此,在杀死所有其他孩子之后,pkill 命令将定期终止。
【解决方案2】:

启动每个子进程后,可以通过

ID=$!

然后您可以使用存储的 PID 来查找并杀死所有孙子等进程,如 herehere 所述。

【讨论】:

  • 只是补充一点,如果监视器的子进程可以产生子子进程等,您需要递归地使用链接中描述的技术。
  • @David 提到的第二页包含一个解决方案。
  • 是的,但是当我写评论时,第二个链接还没有出现。
  • 第一个链接失效了,彼得。
【解决方案3】:

如果您使用带有kill 的负PID,它将杀死一个进程组。示例:

kill -- -1234

【讨论】:

    【解决方案4】:

    扩展 pihentagy 的答案以递归地杀死所有后代(不仅仅是孩子):

    kill_descendant_processes() {
        local pid="$1"
        local and_self="${2:-false}"
        if children="$(pgrep -P "$pid")"; then
            for child in $children; do
                kill_descendant_processes "$child" true
            done
        fi
        if [[ "$and_self" == true ]]; then
            kill -9 "$pid"
        fi
    }
    

    现在

    kill_descendant_processes $$
    

    将杀死当前脚本/shell的后代。

    (在 Mac OS 10.9.5 上测试。仅依赖 pgrep 和 kill)

    【讨论】:

    【解决方案5】:
    kill $(jobs -p)
    

    Rhys Ulerich的建议:

    注意竞争条件,使用 [下面的代码] 可以完成 Jürgen 的建议,而不会在不存在作业时导致错误

    [[ -z "$(jobs -p)" ]] || kill $(jobs -p)
    

    【讨论】:

    • 警告竞争条件,使用'test -z "`jobs -p`" || kill `jobs -p`' 完成了 Jürgen 的建议,而不会在没有作业存在时引发错误
    【解决方案6】:

    带有选项“-P”的 pkill 应该会有所帮助:

    pkill -P $(pgrep some_monitor1.sh)
    

    来自手册页:

       -P ppid,...
              Only match processes whose parent process ID is listed.
    

    linuxquests.org上有一些讨论,请查看:

    http://www.linuxquestions.org/questions/programming-9/use-only-one-kill-to-kill-father-and-child-processes-665753/

    【讨论】:

    • 如果你已经启动了 2 次呢?一个会杀死其他子进程!
    • @pihentagy 系统上的每个唯一的、正在运行的进程(相对于工具)都有一个唯一的 PID。如果您打开两个终端然后一个进程监视器,监视器将显示两个不同的“sh”进程,每个进程都有自己的 PID。但是,您是对的,您可以通过名称获取进程来破坏事物,这些进程不必是(并且通常不是)唯一的。
    【解决方案7】:

    我喜欢以下简单的方法:使用具有某些名称/值的环境变量启动子进程,然后使用它来终止子进程。最方便的是使用正在运行的 bash 脚本的进程 ID,即 $$。当子进程在继承环境时启动另一个子进程时,这也适用。

    所以像这样启动子流程:

    MY_SCRIPT_TOKEN=$$ some_monitor1.sh &
    MY_SCRIPT_TOKEN=$$ some_monitor2.sh &
    

    然后像这样杀死他们:

    ps -Eef | grep "MY_SCRIPT_TOKEN=$$" | awk '{print $2}' | xargs kill
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-22
      • 2013-01-03
      • 2012-09-08
      • 2019-12-09
      相关资源
      最近更新 更多