使用陷阱确认退出

使用陷阱确认退出

我试图捕获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+时CSIGINT信号将传递到整个前台进程组。这里它被发送到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 中不容易修复(也许通过设置处理程序?)。Cjob_pid=$jobpidwaitSIGCHLD
  • 如果您需要作业的退货状态,则需要使用该wait $job_pid表格。但是你无法区分“wait被信号中断”和“作业被信号杀死”(也不能区分“作业根据返回状态≥128自行终止”,但这是 shell 中的普遍事实编程)。
  • 如果有的话,这不会轻易扩展到多个子作业。请注意,当您超出大多数 shell 实现的基础知识时,陷阱和信号的行为通常会令人惊讶(只有 ksh 做得很好)。

要克服这些限制,请使用 Perl 或 Python 等更高级的语言。

相关内容