Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am trying to trap the Ctrl+C signal asking a confirmation from the user. The trapping part works fine. But once the signal gets trapped, it does not return to the normal execution. Instead, it quits the script. How to make it resume the execution when the user presses no.

here is my code

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 /
share|improve this question
add comment

1 Answer 1

up vote 11 down vote accepted

What's happening

When you press Ctrl+C, the SIGINT signal is delivered to the whole foreground process group. Here it's sent to both the find process and the calling shell process. find reacts by exiting immediately, and the shell reacts by calling the trap.

If the code in the trap returns (i.e. doesn't call exit), execution proceeds with the command after the one that was interrupted by the signal. Here, after the find command comes the end of the script, so the script exits immediately anyway. But you can see the difference between entering 0 and 1 by adding another command:

find /
echo "find returned $?"

A way to do what you want (but probably shouldn't do)

You can do what you want; but this part of my answer is more about discovering shell programming than solving an actual problem.

  • As a design matter, a restartable signal isn't what you'd normally expect in the kind of relatively simple programs that shell scripts normally are. The expectation is that Ctrl+C will kill the script.
  • As you'll see below, it stretches the capabilities of the shell a bit far anyway.

If you want to avoid killing find, you need to start it in the background: find / &. Then use the wait builtin to wait for it to exit normally. A signal will interrupt the wait built-in, which you can run in a loop until you receive a signal you want to propagate. Then use kill to kill the job.

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: $?

There are limitations to this approach in the shell:

  • There's a race condition: if you press Ctrl+C just after the job has finished but before the job_pid= line, the signal handler will try to kill $jobpid, but the process no longer exists (even as a zombie, because wait has already reaped it), and the process ID may have been reused by another process. This isn't easily fixable in shell (maybe by setting a handler for SIGCHLD?).
  • If you need the return status from the job, you need to use the wait $job_pid form. But then you can't distinguish “wait was interrupted by a signal” from “the job was killed by a signal” (nor from “the job terminated of its own accord with a return status ≥128”, but that's a general fact in shell programming).
  • This won't extend easily if at all to multiple subjobs. Note that the behavior of traps and signals is often surprising when you go beyond the basics in most shell implementations (only ksh does this well).

To overcome these limitations, use a fancier language like Perl or Python.

share|improve this answer
    
Thank you Gilles. Sorry for the late response –  CHID Jul 3 '11 at 15:39
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.