我试图捕获Ctrl+C
请求用户确认的信号。捕获部分工作正常。但一旦信号被捕获,它就不会返回到正常执行。相反,它会退出脚本。如何让它在用户按否时恢复执行。
这是我的代码
hell()
{
echo "Do you want to quit? Press 1 for yes and 0 for no";
read n;
if [ $n == 1 ]; then
exit 1;
fi
}
trap "hell" SIGINT
find /
答案1
发生了什么
当您按下Ctrl+时C,SIGINT
信号将传递到整个前台进程组。这里它被发送到find
进程和调用 shell 进程。find
反应是立即退出,shell 反应是调用陷阱。
如果陷阱中的代码返回(即不调用exit
),则继续执行被信号中断的命令之后的命令。在这里,命令之后find
是脚本的结尾,因此脚本无论如何都会立即退出。但是你可以通过添加另一个命令来看到输入 0 和 1 之间的区别:
find /
echo "find returned $?"
一种做你想做的事(但可能不应该做)的方法
你可以做你想做的;但我的这部分答案更多的是关于发现 shell 编程,而不是解决实际问题。
- 作为设计问题,可重新启动信号并不是您通常在 shell 脚本通常所具有的相对简单的程序中所期望的信号。预期Ctrl+C将终止脚本。
- 正如您将在下面看到的,无论如何,它都稍微扩展了 shell 的功能。
如果你想避免杀戮find
,你需要在背景: find / &
。然后使用wait
内置等待其正常退出。信号会中断wait
内置程序,您可以循环运行它,直到收到要传播的信号。然后用kill
杀死该作业。
hell () {
echo "Do you want to quit? Press 1 for yes and 0 for no"
read n
if [ "$n" = 1 ]; then
# Kill the job if it's running, then exit
if [ -n "$job_pid" ]; then kill $job_pid; fi
exit 1
fi
}
job_pid=
trap "hell" SIGINT
# Start a demo job in the background
for i in 1 2 3 4 5; do date; sleep 1; done &
job_pid=$!
# Call wait in a loop; wait will return 0 if the job exits, and 128+$signum if interrupted by a signal.
while ! wait; do
echo "resuming wait"
done
job_pid=
echo last exit code: $?
这种方法在 shell 中存在局限性:
- 有一个竞争条件:如果您在作业完成后但在该行之前按Ctrl+ ,信号处理程序将尝试杀死,但该进程不再存在(即使作为僵尸,因为已经收割了它),并且该进程ID 可能已被另一个进程重用。这在 shell 中不容易修复(也许通过设置处理程序?)。C
job_pid=
$jobpid
wait
SIGCHLD
- 如果您需要作业的退货状态,则需要使用该
wait $job_pid
表格。但是你无法区分“wait
被信号中断”和“作业被信号杀死”(也不能区分“作业根据返回状态≥128自行终止”,但这是 shell 中的普遍事实编程)。 - 如果有的话,这不会轻易扩展到多个子作业。请注意,当您超出大多数 shell 实现的基础知识时,陷阱和信号的行为通常会令人惊讶(只有 ksh 做得很好)。
要克服这些限制,请使用 Perl 或 Python 等更高级的语言。