Bash: 终止子进程中的所有进程

6

在bash中,我可以通过$!变量获取最后一个子进程的进程ID(pid)。然后我可以在它结束之前杀掉这个子进程:

(sleep 5) & pid=$!
kill -9 $pid

这个功能与广告宣传的一样。如果我现在在sleep之后添加更多的命令,即使其他命令没有被执行,sleep命令在子进程被杀死后仍然会继续执行。
例如,考虑以下代码,它启动一个子进程并使用ps监视其终止:
# Start subprocess and get its pid
(sleep 5; echo done) & pid=$!

# grep for subprocess
echo "grep before kill:"
ps aux | grep "$pid\|sleep 5"

# Kill the subprocess
echo
echo "Killing process $pid"
kill -9 $pid

# grep for subprocess
echo
echo "grep after kill:"
ps aux | grep "$pid\|sleep 5"

# Wait for sleep to finish
sleep 6

# grep for subprocess
echo
echo "grep after sleep is finished:"
ps aux | grep "$pid\|sleep 5"

如果我将此保存到名为filename的文件中并运行它,我会得到以下打印输出:
grep before kill:
username    7464    <...>    bash filename
username    7466    <...>    sleep 5
username    7467    <...>    grep 7464\|sleep 5

Killing process 7464

grep after kill:
username    7466    <...>    sleep 5
username    7469    <...>    grep 7464\|sleep 5

grep after sleep is finished:
username    7472    <...>    grep 7464\|sleep 5

ps 命令中不重要的信息将被替换为 <...>。看起来 kill 已经杀死了 filename 的整个 bash 执行过程,同时保留了 sleep 运行。

我该如何正确地杀死整个子进程?


提示:它还在那里吗? - Karoly Horvath
@KarolyHorvath 还在吗? - jmd_dk
3个回答

4

在退出之前,您可以在子Shell中设置一个陷阱来终止任何活动作业:

 (trap 'kill $(jobs -p)' EXIT; sleep 5; echo done ) & pid=$!

这对我不起作用。jobs -p 应该输出什么?当我运行它时,它没有输出任何内容。 - jmd_dk
1
啊,我忘了提一个重要的点:你不能使用 kill -9,因为这会导致操作系统立即杀死进程,而不给它运行 EXIT 处理程序的机会。只使用 kill 就足够了。 - chepner
jobs -p 列出由 shell 管理的当前所有活动作业的进程 ID。 - chepner
是的,没有使用-9选项来强制终止进程,这个命令确实可以工作。这也解释了为什么之前jobs -p没有输出任何内容(因为该命令从未运行)。 - jmd_dk
在正常工作中,您不应该使用 kill -9 命令;它是用来终止那些无法通过正常手段关闭的有缺陷的程序。 - chepner

3

我不确切知道为什么那个睡眠进程会变成孤儿,但无论如何,你可以使用带有 -P 标志的 pkill 来代替 kill 命令,以杀死所有子进程。

pkill -TERM -P $pid

编辑: 这意味着要杀死一个进程及其所有子进程,您应该使用以下命令。

CPIDS=`pgrep -P $pid` # gets pids of child processes
kill -9 $pid
for cpid in $CPIDS ; do kill -9 $cpid ; done

Shell通常不会将它们接收到的信号发送给它们的子进程。但这并不明显,因为在终端上运行sleep时,子进程会直接接收到该信号。 - erik258
这确实会终止 sleep,但有趣的是 echo done 会被执行。我可以通过使用 && 将子进程中的命令链接在一起来解决这个问题,从而得到一个可行的解决方案。 - jmd_dk
@jmd_dk - 我之前说的不对,我说它会杀死所有子进程,实际上它只会杀死所有子进程,我会编辑我的回答。 - MacHala

1
你可以看一下 rkill,它似乎符合你的要求:

http://www.unix.com/man-page/debian/1/rkill/

rkill [-SIG] pid/name...

当作为rkill调用时,此实用程序不会显示有关进程的信息,而是发送信号给它们。如果在命令行上未指定,则发送终止(SIGTERM)信号。


可以运行,但 rkill/pslist 看起来并不是大多数 Linux 系统上安装的标准工具。 - jmd_dk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接