使用陷阱确认退出


9

我正在尝试捕获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 /

Answers:


12

发生了什么

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

如果陷阱中的代码返回(即未调用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+ ,信号处理程序将尝试kill ,但是该过程不再存在(即使是僵尸,因为已经收获了它),并且该过程ID可能已被另一个进程重用。这在外壳中不容易修复(可能是通过为?设置处理程序)。Cjob_pid=$jobpidwaitSIGCHLD
  • 如果您需要工作的返回状态,则需要使用wait $job_pid表格。但是,那么您就无法将“ wait被信号打断”与“该工作被信号杀死”(也无法区分“ 该工作以返回状态≥128自行终止”),但这是shell中的普遍事实编程)。
  • 如果完全扩展到多个子作业,这将很难扩展。请注意,当您超出大多数shell实现的基础知识时,陷阱和信号的行为通常令人惊讶(只有ksh可以做到这一点)。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.