如何杀死shell的所有子进程?

Posted

技术标签:

【中文标题】如何杀死shell的所有子进程?【英文标题】:How to kill all subprocesses of 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 并不总是有帮助。

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

【问题讨论】:

一个很棒的帖子:***.com/questions/392022/… 【参考方案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 来查找并杀死所有孙子等进程,如 here 或 here 所述。

【讨论】:

只是补充一点,如果监视器的子进程可以产生子子进程等,您需要递归地使用链接中描述的技术。 @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)

【讨论】:

好的,但请不要使用kill -9“默认”,更改进程退出! serverfault.com/a/415744/55046 最佳答案。谢谢。【参考方案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

【讨论】:

以上是关于如何杀死shell的所有子进程?的主要内容,如果未能解决你的问题,请参考以下文章

无法弄清楚如何在 Python 3.5 中杀死子进程 [重复]

如何在 python 中杀死使用 subprocess.call 调用的子进程? [复制]

如何终止使用 shell=True 启动的 python 子进程

主进程退出的时候,杀死所有子进程

如何使用子进程模块杀死(或避免)僵尸进程

杀死 less(1) 子进程后输入失败