当我按 Ctrl+C 键时,如何终止 shell 脚本并等待后台进程完成?

当我按 Ctrl+C 键时,如何终止 shell 脚本并等待后台进程完成?

我正在尝试设置一个 shell 脚本,以便它运行后台进程,当我按 ctrl+C 键运行 shell 脚本时,它会终止子进程,然后退出。

我所能想到的最好的办法就是这个。看来kill 0 -INT在等待发生之前也会终止脚本,因此 shell 脚本会在子脚本完成之前终止。

关于如何让这个 shell 脚本在发送 INT 后等待子进程死亡,有什么想法吗?

#!/bin/bash
trap 'killall' INT

killall() {
    echo **** Shutting down... ****
    kill 0 -INT
    wait # Why doesn't this wait??
    echo DONE
}

process1 &
process2 &
process3 &

cat # wait forever

答案1

首先,你应该知道kill 0向当前进程组中的所有进程发送信号。(参见kill(1) - Linux 手册页) 我猜是它终止的进程比它应该终止的进程多。这就是为什么它wait不是在等待,而是出于某种原因被终止。解决这个问题的最佳方法是逐个终止正在运行的作业jobs -pr:这样我就可以确保信号只发送给当时的子进程,而不会发送给其他进程。

为了实现这个功能,我做了几个测试,但一直无法向子进程发送 SIGINT。它们根本无法对此做出反应!在网上搜索后,我在 Stack Overflow 上找到了这个答案:

https://stackoverflow.com/questions/2524937/how-to-send-a-signal-sigint-from-script-to-script-bash

所以真正的问题是无法将 SIGINT 从一个脚本发送到另一个脚本,因为在非交互式 shell 中信号会被忽略。我无法解决这个问题(使用bash -i调用子脚本不起作用)。

我知道您可能想要发送 SIGINT 并等待子进程正常关闭,但如果您不介意使用 SIGTERM(或除 SIGINT 之外的任何其他信号),这是我编写的最佳脚本:

#!/bin/bash
trap 'killall' INT

killall() {
    echo '**** Shutting down... ****'
    jobs -pr
    for i in $(jobs -pr); do
        kill -TERM $i
    done
    wait
    echo DONE
}

./long.sh &
./long.sh &
./long.sh &

cat # wait forever

为了测试,我创建了这个long.sh脚本:

#!/bin/bash
trap 'killall' TERM

echo "Started... $$" >> file.txt

killall() {
    # shutting down gracefully
    echo "Finished... $$" >> file.txt
    exit # don't forget this!
}

# infinite loop
while true; do echo loop >/dev/null ; done

在最后一个脚本中,我没有使用sleep函数,因为它会创建一个新进程,该进程即使在脚本完成后仍停留在内存中。在脚本中,您可以调用新进程,但应确保向所有进程广播 SIGTERM(或您使用过的任何其他信号)。

答案2

这似乎有效。请确保使用“/usr/bin/kill”,而不是 Bash 内置的“kill”。

[myles@marklar ~]$ /usr/bin/kill --version
kill from util-linux-2.13-pre7

请注意,非交互式 Bash shell(例如脚本)中未启用作业控制。

#!/bin/bash

trap kill_jobs 2

kill_jobs()
{
        while /usr/bin/kill 0; do :; done
        exit 0
}

foo &
foo &
foo &

sleep 777777

相关内容