繁体   English   中英

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

[英]bash: Possible to require double Ctrl-c to to exit a script?

结束目标:等待后台作业完成的BASH脚本不会在第一个Ctrl-c中止; 相反,它需要第二个Ctrl-c退出。

我很清楚BASH内置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

因此,当您按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

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

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

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

    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

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

我有一个稍微不同的用例,并希望将解决方案留在这里,因为谷歌引导我进入这个主题。 您可以继续运行命令,并允许用户使用一个CTRL+C重新启动它,并使用双CTRL+C以下列方式将其终止:

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

一位同事(格雷加)刚刚给了我一个解决方案......我无法相信我没有先想到它。

“我的方法是......使用一个永远不会返回的函数或另一个函数(另一个等待?),将它永久地搁置,可能是永远的,以便第二个处理程序可以正常工作。”

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

#!/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
  1. 在允许信号原始函数发生之前使用它来执行任意命令(例如,陷阱cmd 2,其中cmd 在父脚本因SIGINT而被中断之前运行

上面的斜体部分是不正确的。 运行陷阱处理程序而不是让SIGINT(或其他)中断进程。 更精确地:

  • SIGINT(以及大多数,但不是全部,其他信号)的默认操作是终止进程
  • trap "command" SIGINT导致运行command而不是默认操作( 不如

因此,安装了SIGINT处理程序后,SIGINT不会中断整个脚本。 但它中断wait命令。 陷阱处理程序完成后,脚本会在wait后恢复,即它从结束时退出并正常退出。 你可以通过添加一些调试代码来看到这个:

echo Waiting
wait
echo Back from wait
exit 55                   # Arbitrary value that wouldn't otherwise occur

此版本产生以下内容:

$ foo
Waiting
^CThere are background jobs still running
Hit Ctrl-c again to cancel all bg jobs & quit
back from wait
$ echo $?
55
$ 

您需要做的是在处理程序返回后重复wait 这个版本:

#!/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
        exit 1
    fi
}
f() { sleep 5m; }
trap cleanup_bg_jobs 2
bg_jobs=
for job in 1 2 3; do
    f &
    bg_jobs+=" $!"
done

while [ 1 ]; do
    echo Waiting
    wait
    echo Back from wait
done

按你的要求做:

$ ./foo
Waiting
^CThere are background jobs still running
Hit Ctrl-c again to cancel all bg jobs & quit
Back from wait
Waiting
^CAborting background jobs
  Killing 24154
  Killing 24155
  Killing 24156
$ 

笔记:

  • 我已经离开了调试的东西; 显然你会在生产中删除它
  • 处理程序现在在终止子进程后exit 1 这就是突破无限主循环的原因

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM