杀死由Bash脚本

时间:2017-10-01 14:50:50

标签: linux bash shell command-line

我有许多bash脚本执行很多类似的任务,他们使用一些外部二进制程序。问题是二进制程序通常不会像它们应该那样终止。由于我的脚本运行了数千次,因此很快发生这些进程的许多空闲/接近死亡的实例正在累积。我无法修复这些程序,因此我需要确保我的bash脚本正在终止它们。

SE中已经有一些主题涉及终止bash脚本过程的任务。我已经应用并测试了那里写的内容,并且在某种程度上它起作用了。但它对我的情况来说效果不好,我不明白为什么,因此我开了一个新问题。

我的脚本有一个层次结构,这里以简化的方式显示: 脚本A调用脚本B,脚本B并行调用脚本C的多个实例以使用所有CPU。例如。脚本B并行运行脚本C的5个实例,当脚本C的一个实例完成时,它启动一个新的脚本C,总共运行数千个脚本C.脚本C调用几个不会终止的外部二进制文件/命令很好。它们在后台并行并相互通信。

但是,我的脚本C能够检测外部命令何时完成其工作,即使它们尚未终止,然后我的bash脚本退出。

为了在完成bash脚本期间终止所有外部程序,我添加了一个退出陷阱:

# Exit cleanup
cleanup_exit() {
    # Running the termination in an own process group to prevent it from preliminary termination. Since it will run in the background it will not cause any delays
    setsid nohup bash -c "
        touch /tmp/trace_1  # To see if this code was really executed to this point

        # Trapping signals to prevent that this function is terminated preliminary
        trap '' SIGINT SIGQUIT SIGTERM SIGHUP ERR
        touch /tmp/trace_2  # To see if this code was really executed to this point

        # Terminating the main processes
        kill ${pids[@]} 1>/dev/null 2>&1 || true
        touch /tmp/trace_3
        sleep 5
        touch /tmp/trace_4
        kill -9 ${pids[@]} 1>/dev/null 2>&1 || true
        touch /tmp/trace_5

        # Terminating the child processes of the main processes
        echo "Terminating the child processes"
        pkill -P ${pids[@]} 1>/dev/null 2>&1 || true
        touch /tmp/trace_6
        sleep 1
        pkill -9 -P ${pids[@]} 1>/dev/null 2>&1 || true
        touch /tmp/trace_7

        # Terminating everything else which is still running and which was started by this script
        pkill -P $$ || true
        touch /tmp/trace_8
        sleep 1
        pkill -9 -P $$ || true
        touch /tmp/trace_9
    "
}
trap "cleanup_exit" SIGINT SIGQUIT SIGTERM EXIT

现在,如果我并行运行非常少的脚本C实例,这似乎有效。如果我将数字增加到更多,例如10(工作站功能强大,应该能够并行处理脚本C和外部程序的几十个并行实例),然后它不再起作用,并且数百个外部程序实例正在快速累积。

但我不明白为什么。例如,累积的其中一个进程的PID是32048.在日志中我可以看到退出陷阱的执行:

+ echo ' * Snapshot 190 completed after 3 seconds.'
 * Snapshot 190 completed after 3 seconds.
+ break
+ cleanup_exit
+ echo

+ echo ' * Cleaning up...'
 * Cleaning up...
+ setsid nohup bash -c '
        touch /tmp/trace_1  # To see if this code was really executed to this point

        # Trapping signals to prevent that this function is terminated preliminary
        trap '\'''\'' SIGINT SIGQUIT SIGTERM SIGHUP ERR
        touch /tmp/trace_2  # To see if this code was really executed to this point

        # Terminating the main processes
        kill 31678' '32048 1>/dev/null 2>&1 || true
        touch /tmp/trace_3
        sleep 5
        touch /tmp/trace_4
        kill -9 31678' '32048 1>/dev/null 2>&1 || true
        touch /tmp/trace_5

        # Terminating the child processes of the main processes
        pkill -P 31678' '32048 1>/dev/null 2>&1 || true
        touch /tmp/trace_6
        sleep 1
        pkill -9 -P 31678' '32048 1>/dev/null 2>&1 || true
        touch /tmp/trace_7

        # Terminating everything else which is still running and which was started by this script
        pkill -P 31623 || true
        touch /tmp/trace_8
        sleep 1
        pkill -9 -P 31623 || true
        touch /tmp/trace_9
    '

显然,此过程的PID用于退出陷阱,但该过程未退出。为了测试我在这个过程中再次手动运行kill命令,然后它确实退出了。

最有趣的是,只显示最高为5的跟踪文件。没有超过5,但为什么?

更新:我刚刚发现,即使我并行运行脚本C的一个实例,即顺序,它只能运行一段时间。突然在某个时间点,进程不再被终止,但是它们开始永远地停留并积累。机器不应该并行过载一个过程。在我的日志文件中,退出陷阱仍然像以前一样正确调用,没有区别。内存也是免费的,CPU也是部分免费的。

1 个答案:

答案 0 :(得分:5)

对任何shell脚本进行良好的健全性检查是在其上运行ShellCheck:

.xib

事实上,你的xtrace在这一行上做了一些奇怪的事情:

Line 9:
        kill ${pids[@]} 1>/dev/null 2>&1 || true
             ^-- SC2145: Argument mixes string and array. Use * or separate argument.

此处的问题是,您的kill 31678' '32048 1>/dev/null 2>&1 || true ^^^--- What is this? 扩展为多个单词,${pids[@]}仅解释第一个单词。这是一个简化的例子:

bash -c

这最终写出pids=(2 3 4) bash -c "echo killing ${pids[@]}" 而没有提到3或4.这相当于运行

killing 2

其他pid只是成为位置参数bash -c "echo killing 2" "3" "4" $0而不是执行命令的一部分。

相反,与ShellCheck suggested一样,您希望$1将所有pid与空格连接起来并将它们作为单个参数插入:

*

打印pids=(2 3 4) bash -c "echo killing ${pids[*]}"