bash:可能需要双Ctrl-c才能退出脚本?

rsa*_*saw 7 linux bash shell

结束目标:等待后台作业完成的BASH脚本首先不会中止Ctrl-c; 相反,它需要一秒钟Ctrl-c退出.

我很清楚BASH-builtin的trap工作原理.你可以:

  1. 用它来完全忽略一个信号(例如trap '' 2)......或

  2. 在允许信号原始函数发生之前使用它来执行任意命令(例如,在父脚本因中断而运行的trap cmd 2地方cmd运行SIGINT)

所以问题归结为:

我怎样才能有效地结合1&2一起,即防止最终结果的信号会导致(1 -例如,停止脚本由于取消SIGINT),同时也使该信号引起其他的东西(2 -例如,递增计数器,检查计数器并有条件地打印警告或退出).

更简单地说:

我怎样才能发出信号呢?不只是在它完成它之前插入一个工作.

这是一些示例代码,用于演示我的目标; 然而,它当然不起作用 - 因为trap只能从上面做12.

#!/bin/bash
declare -i number_of_times_trap_triggered
cleanup_bg_jobs() {
    number_of_times_trap_triggered+=1
    if [[ ${number_of_times_trap_triggered} -eq 1 ]]; then
        echo "There are background jobs still running"
        echo "Hit Ctrl-c again to cancel all bg jobs & quit"
    else
        echo "Aborting background jobs"
        for pid in ${bg_jobs}; do echo "  Killing ${pid}"; kill -9 ${pid}; done
    fi
}
f() { sleep 5m; }
trap cleanup_bg_jobs 2
bg_jobs=
for job in 1 2 3; do
    f &
    bg_jobs+=" $!"
done
wait
Run Code Online (Sandbox Code Playgroud)

所以这是你按下Ctrl-c一次后最终获得的输出.

[rsaw:~]$ ./zax 
^CThere are background jobs still running
Hit Ctrl-c again to cancel all bg jobs & quit
[rsaw:~]$ ps axf|tail -6 
24569 pts/3    S      0:00 /bin/bash ./zax
24572 pts/3    S      0:00  \_ sleep 5m
24570 pts/3    S      0:00 /bin/bash ./zax
24573 pts/3    S      0:00  \_ sleep 5m
24571 pts/3    S      0:00 /bin/bash ./zax
24574 pts/3    S      0:00  \_ sleep 5m
Run Code Online (Sandbox Code Playgroud)

当然我可以修改它来清理第一个工作Ctrl-c,但这不是我想要的.我想在触发第一个陷阱后停止BASH退出......直到第二次触发它为止.

PS:目标平台是Linux(我不太关心POSIX合规性)与BASH v4 +

jkg*_*yti 8

我有一个稍微不同的用例,想把解决方案留在这里,因为谷歌把我带到了这个话题。您可以继续运行命令,并允许用户使用 one 重新启动它CTRL+C,并CTRL+C通过以下方式使用 double 杀死它:

trap_ctrlC() {
    echo "Press CTRL-C again to kill. Restarting in 2 second"
    sleep 2 || exit 1
}

trap trap_ctrlC SIGINT SIGTERM

while true; do  
    ... your stuff here ...
done
Run Code Online (Sandbox Code Playgroud)


小智 5

我在这里做了类似的事情,它主要分解为:

    ATTEMPT=0
handle_close() {
    if [ $ATTEMPT -eq 0 ]; then
        ATTEMPT=1
        echo "Shutdown."
    else
        echo "Already tried to shutdown. Killing."
        exit 0
    fi
}
trap handle_close SIGINT SIGTERM
Run Code Online (Sandbox Code Playgroud)

您可以在处理程序中设置一个变量,以便下次被捕获时再次检查。


rsa*_*saw 1

一位同事(Grega)刚刚给了我一个解决方案......好吧,我不敢相信我没有首先想到它。

“我的方法是......将其搁置足够长的时间,可能永远搁置,使用一个永远不会返回的函数或其他东西(另一个等待?),以便第二个处理程序可以正确地完成其工作。”

根据记录,wait这里不起作用。(递归。)但是,sleep向我的原始代码的cleanup_bg_jobs()函数添加一个命令会解决它..但会导致孤立的进程。因此,我利用进程组来确保脚本的所有子进程确实被杀死。后代的简化示例:

#!/bin/bash
declare -i count=
handle_interrupt() {
    count+=1
    if [[ ${count} -eq 1 ]]; then
        echo "Background jobs still running"
        echo "Hit Ctrl-c again to cancel all bg jobs & quit"
        sleep 1h
    else
        echo "Aborting background jobs"
        pkill --pgroup 0
    fi
}
f() { tload &>/dev/null; }
trap handle_interrupt 2
for job in 1 2 3; do
    f &
done
wait
Run Code Online (Sandbox Code Playgroud)