【发布时间】:2023-03-18 05:15:01
【问题描述】:
我有一个 Bash 脚本(Bash 3.2、Mac OS X 10.8)并行调用多个 Python 脚本,以便更好地利用多个内核。每个 Python 脚本都需要很长时间才能完成。
问题是,如果我在 Bash 脚本中间按 Ctrl+C,Python 脚本实际上并没有被杀死。我如何编写 Bash 脚本以便杀死它也会杀死它的所有背景子项?
这是我原来的“简化测试用例”。不幸的是,我似乎已经减少了很多,以至于它不再证明问题了;我的错。
set -e
cat >work.py <<EOF
import sys, time
for i in range(10):
time.sleep(1)
print "Tick from", sys.argv[1]
EOF
function process {
python ./work.py $1 &
}
process one
process two
wait
这是一个完整的测试用例,仍然高度简化,但希望这个可以证明问题。它在我的机器上重现......但是,两天前我认为 old 测试用例在我的机器上重现,今天它绝对没有。
#!/bin/bash -e
set -x
cat >work.sh <<EOF
for i in 0 1 2 3 4 5 6 7 8 9; do
sleep 1; echo "still going"
done
EOF
chmod +x work.sh
function kill_all_jobs { jobs -p | xargs kill; }
trap kill_all_jobs SIGINT
function process {
./work.sh $1
}
process one &
wait $!
echo "All done!"
即使在 Ctrl+C 之后,此代码仍会继续打印 still going。但是,如果我将& 从外部process 移动到内部(即:./work.sh $1 &),那么 Ctrl+C 将按预期工作。我完全不明白!
在我的真实脚本中,process 包含多个命令,并且命令是长时间运行的,必须按顺序运行;所以在这种情况下,我不知道如何“将& 移动到process 中”。我确信这是可能的,但它必须是不平凡的。
$ bash --version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin12)
Copyright (C) 2007 Free Software Foundation, Inc.
编辑:非常感谢@AlanCurry 教我一些 Bash 的东西。不幸的是,我仍然不明白我的示例中到底发生了什么,但这实际上是一个有争议的问题,正如 Alan also 有帮助地指出,对于我现实世界的并行化问题,Bash 是错误的工具,并且我应该使用带有make -j3 的简单makefile! make 尽可能并行运行,也完美理解 Ctrl+C;问题已解决(即使问题未得到解答)。
【问题讨论】:
-
trap应该可以在 shell 函数中正常工作。我拿了你的脚本,在顶部添加了你的killstuff定义和trap killstuff SIGINT命令,用 bash 运行它,它运行良好。不过,我使用的是 Linux,而不是 MacOS,所以也许这就是你的行为不同的原因。尝试在顶部使用set -x运行,它会生成一些调试输出,您可以在此处发布。 -
@AlanCurry:你说得对,我的旧测试用例实际上并没有重现这个问题。 :( 昨天我以为是这样,但我一定把它的几个不同版本混在一起了。正如我的新测试用例所示,即使是一个字符也会对脚本的行为产生很大影响。所以,想再试一次是吗?
-
我还没有一个好的解决方案,但我可以解释发生了什么。当您在后台运行 shell 函数时,它会在子 shell(一个单独的进程)中运行。所以你有 3 个进程正在运行:运行脚本的主 shell、运行函数的子 shell 和 python 进程。您的 kill_all_jobs 函数将 SIGTERM 发送到子shell,杀死它但不杀死孙子 python 进程。所有这一切都是必要的,因为python的默认 SIGINT 处理程序拒绝在按下原始 ^C 时死亡。 (python 在前台进程组中,所以它确实得到一个 SIGINT)
-
但是我的新测试用例根本不使用 Python,所以这不可能是全部故事......?不过,这肯定与无法杀死孙子进程有关。
-
进一步的研究表明,主要问题是 SIGINT 通常被后台进程忽略。在主脚本退出后让他们继续运行是一个有意的功能,而且很难解决。您甚至不能将
trap - 2放在 work.sh 中,因为内置的 trap 拒绝取消忽略脚本启动时被忽略的信号。
标签: bash shell process wait kill