in reply to Have $SIG{INT} ask if user wants to quit

While I am not certain I suspect that the problem arises because the signal is interrupting the read on STDIN then in the signal handler you are reading from STDIN and then, if the user chooses not to quit, the original read resumes, but now its input buffers have been confused by the read in the interrupt handler.

One way to avoid reading from STDIN in the signal handler is as follows:

#!/usr/bin/perl use strict; use warnings; foreach my $prompt ( "eat tacos for sustenance", "eat ham for sustenance", ) { eval { local $SIG{INT} = sub { die "caught sig\n"; }; print "$prompt: "; chomp(my $ans=<STDIN>); print "\nans = $ans\n"; }; if($@ eq "caught sig\n") { print "\nDo you really want to quit? "; my $ans = <STDIN>; exit(1) if($ans =~ m/^y/i); redo; } }

In this case, the signal handler terminates the eval, terminating the read. The following code then prompts the user and either terminates the program or repeats the loop block, depending on the answer.

Replies are listed 'Best First'.
Re^2: Have $SIG{INT} ask if user wants to quit
by walkingthecow (Friar) on Mar 11, 2009 at 17:10 UTC
    I completely agree with you! Here's an example of what you mean below. If control-c is captured when asking for input, then script will crash. However, if control-c is captured during output, everything goes as expected... I guess I am just breaking stdin and it does not know how to get back there... I'm not sure how in my own code to remedy this, since there are a lot of prompts, all broken by output.
    #!/usr/bin/perl -w $SIG{'INT'} = 'catch_int'; sub catch_int { my $int_ans; system("\n"); while ($int_ans !~ /[Nn]/) { print "Do you want to exit [y or n]? "; $int_ans=<STDIN>; if ($int_ans =~ /[Yy]/) { die "\nCaught SIGINT -- Program stopped by user\n"; } } } print "Please enter a choice: "; chomp(my $choice=<STDIN>); if ($choice =~ /[A-Z]+/i) { for (my $i = 0; $i < 10000; $i++) { print "Good choice!\n"; } }

      First you should make sure you are very familiar with Signals. Then you need to carefully consider the quirks of your platform(s). There are many, sometimes subtle and sometimes critical differences between the various operating systems.

      There are two possibilities that will generally be "safe": the first is to do nothing but set a flag in your interrupt handler and take the appropriate action elsewhere; the other is to die in your signal handler and, where appropriate, run code in an eval to handle the abnormal termination, as in the example I showed previously. Doing anything else in the signal handler tends to be problematic and tricky, as you are finding out.

      If your main program consists of a tight loop with rapid iterations, the first approach can work well. Just add a test for the flag in the main loop and it will be tested reasonably soon after the signal is caught.

      If you have some long running sections of code in which it is inconvenient to scatter checks for a flag and you want a prompt response to ^C then the second approach is probably necessary. It allows you to terminate long running sections without explicit coding within the section.

      If the main problem is interrupts while prompting for interactive input, as in your example, then I suggest creating a subroutine to prompt and collect the response with the subroutine implemented similarly to my previous example. While it may be tedious to revise your program if there are many instances, it will be quite straight forward and will not require significant change to the structure.

      Otherwise, you can investigate ways to restore the I/O layers (which may differ depending on platform and perl compile and run-time options) to a functional state after doing I/O in the signal handler. I don't know enough about the I/O implementations to do anything other than steer clear of I/O in signal handlers myself. Sorry...

      If you can post representative samples of your actual code, you may get some further helpful suggestions.