使用陷阱确认退出

CHI*_*HID 9 shell scripting signals

我试图捕获Ctrl+C要求用户确认的信号。诱捕部分工作正常。但是一旦信号被捕获,它就不会恢复正常执行。相反,它会退出脚本。如何让它在用户按下 no 时恢复执行。

这是我的代码

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 /
Run Code Online (Sandbox Code Playgroud)

Gil*_*il' 12

发生了什么

当您按下Ctrl+ 时CSIGINT信号被传递到整个前台进程组。在这里,它被发送到find进程和调用 shell 进程。find通过立即退出做出反应,外壳通过调用陷阱做出反应。

如果陷阱中的代码返回(即不调用exit),则执行被信号中断的命令之后的命令。在这里,在find命令结束后,脚本无论如何都会立即退出。但是你可以通过添加另一个命令来查看输入 0 和 1 的区别:

find /
echo "find returned $?"
Run Code Online (Sandbox Code Playgroud)

一种做你想做的事(但可能不应该做)

你可以做你想做的; 但这部分我的回答更多是关于发现 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: $?
Run Code Online (Sandbox Code Playgroud)

这种方法在 shell 中有一些限制:

  • 存在竞争条件:如果您在作业完成后但在行之前按Ctrl+ ,信号处理程序将尝试 kill ,但该进程不再存在(即使作为僵尸,因为已经收割了它),并且该进程ID 可能已被另一个进程重用。这在 shell 中不容易修复(也许通过为?设置处理程序)。Cjob_pid=$jobpidwaitSIGCHLD
  • 如果您需要工作的退货状态,则需要使用wait $job_pid表格。但是你无法区分“wait被信号中断”和“工作被信号杀死”(也不能从“工作自行终止并返回状态?128”,但这是shell中的一般事实编程)。
  • 如果完全扩展到多个子作业,这将不会轻易扩展。请注意,当您超越大多数 shell 实现的基础知识时,陷阱和信号的行为通常会令人惊讶(只有 ksh 做得很好)。

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