我运行的是 Ubuntu 16.04
我有这个进程 X 从多个 tty 运行。我使用 screen 命令从其他伪终端运行它,它也从 crontab 运行。
该程序是从 python 脚本启动的,而 python 脚本是从 bash 脚本启动的。
有时,python 脚本会出现一个我无法捕获的异常,并且它会使该进程 X 继续运行。
我想在 bash 脚本末尾终止此进程 X,但避免终止从其他终端运行的其他 X 进程。
我想过使用 pgrep 来处理 X,它与使用 -t 参数的其他终端无关,但我无法理解文档中的语法。
答案1
为Python代码启动一个新的进程组(使用setsid
实用程序)以及它启动的任何其他进程(包括应用程序本身),因此您可以根据需要杀死整个进程组。
您可以使用以下构造来执行此操作:
exec 3>&1
pgid=$(exec setsid bash -c 'echo $$; exec >&3-; exec COMMAND...')
哪里COMMAND...
是您通常启动的命令及其参数。请注意,它位于单引号内,并且要运行的命令必须可作为字符串进行计算(而不是普通的 shell 表达式)。
第一个重定向3>&1
将标准输出描述符复制到描述符 3。重定向将>&3-
描述符 3 移动到标准输出。
执行, 并计算写入标准输出的数据$(...)
。...
上面,我们将标准输出读入 shell 变量中pgid
。
子 shell ( ...
) 替换为setsid
实用程序,它在新会话(进程组)中执行其参数。在这里,它执行bash
,打印出当前进程 PID( $$
),将原始标准输出从描述符 3 移回,并替换自身/执行所需的COMMAND...
。
shell 将在运行期间执行该行,并且只有在自身退出COMMAND...
后才会前进到下一行。COMMAND...
如果COMMAND...
虚假进程仍在运行,并且您想杀死它们,您所需要做的就是运行
kill -SIGNAL -$pgid
问题是发送哪个信号。KILL
会立即杀死进程组中剩余的进程,因此这是最简单的选择。但是,如果进程表现良好,您应该能够问通过向他们发送信号来让他们退出TERM
。当然,有时进程可能会处于不稳定状态,因此它们不会做出反应TERM
并且确实需要进行KILL
编辑。为了解决这个问题,您可以使用一个小循环,并ps
查看是否还有剩余进程:
retries=50
while ((1)); do
# Processes left?
left=$(ps -o pid= -s $pgrp)
[ -n "$left" ] || break
# Ask them to terminate.
kill -TERM $left
# Wait 0.1 seconds.
sleep .1
# Decrement the retry counter.
((--retries > 0)) || break
done
# If there are any processes left in the group,
# send them a KILL signal.
left=$(ps -o pid= -s $pgrp)
[ -n "$left" ] && kill -KILL $left
请注意,50 次重试(每次重试 0.1 秒)意味着在发送终止信号之前最多仅等待 5 秒。这可能不是一个合适的值,具体取决于应用程序及其运行的硬件类型。例如,如果机器是笔记本电脑,并且应用程序保存一些历史记录或登录到多个文件中,并且驱动器恰好在退出点休眠,那么我会将延迟增加到大约 15 到 30 秒。