http://qs321.pair.com?node_id=722571

saintmike has asked for the wisdom of the Perl Monks concerning the following question:

I've always found it annoying that if you start a Perl program like
#!/usr/local/bin/perl system("some_long_running_shell_command"); some_other_stuff();
from the shell and you want to terminate it by using CTRL-C, you have to hit CTRL-C twice.

It gets worse if you run a loop like

#!/usr/local/bin/perl for (1..10) { system("some_long_running_shell_command"); } some_other_stuff();
then you have to hit CTRL-C ten times in a row to terminate the program!

You can easily reproduce this behavior by running

#!/usr/local/bin/perl system("sleep 10"); sleep 10;
which requires two CTRL-C key strokes to terminate. On the other hand, if you use backquotes, as in
#!/usr/local/bin/perl `sleep 10`; sleep 10;
then one CTRL-C suffices. In both cases, both the perl program and the executed shell script are running in the same process group, so the shell will send SIGINT to both processes if you hit CTRL-C once.

However, in the system() case, perl performs some magic to block the SIGINT signal before it execs the child (pp_system in util.c):

#ifndef PERL_MICRO rsignal_save(SIGINT, (Sighandler_t) SIG_IGN, &ihand); rsignal_save(SIGQUIT, (Sighandler_t) SIG_IGN, &qhand); #endif
So what happens is that the shell sends SIGINT to both parent and child, but perl choses to only terminate the child, while the parent keeps running.

Why is it doing that?

Replies are listed 'Best First'.
Re: system() requires double ctrl-c (cases)
by tye (Sage) on Nov 10, 2008 at 07:48 UTC

    Yes, I've been annoyed by that as well.

    But what if you wanted to kill just the child and keep the parent running?

    Obviously, the best thing to do is to just ask whether you wanted to kill them both or instead wanted to leave the parent running. Which is why "superior" operating systems do just that:

    Terminate batch job (Y/N)?

    Except that gets annoying as well. :)

    Other than the case of system in a loop, requiring two CTRL-Cs is a reasonable compromise way of letting you say "kill them both" while still allowing you to say "just kill the child".

    Which leads to the most annoying thing about the "Terminate batch job (Y/N)?" question: It interprets CTRL-C as "No"! Grr.

    In the case of system in a loop, it is probably a good idea to notice that your child was terminated with SIGINT and prompt the user or even just sleep for a fraction of a second so that one can get that second CTRL-C in fast enough.

    - tye        

      I just wind up hammering ctrl-c till the prompt comes back (sometimes whilst muttering obscenities just slightly too audibly for an open plan office).

      But what if you wanted to kill just the child and keep the parent running?

      From the perspective of the end user, this distinction doesn't make much sense. If I run a command, and then hit ctrl-c, then I want it to kill the command I ran. I just don't care how that command was implemented: whether it was implemented with a single process or a whole tree of them.

      (I might appreciate special handling of ctrl-c if the command I ran provides some sort of interactive environment (eg a command line text editor). That would probably need to be considered on a case-by-case basis.)

      Many annoying things. Even the weather is abysmal today. Grr.

      This post was brought to you by the adjectives "grumpy" and "wet".

      --
      .sig : File not found.

Re: system() requires double ctrl-c
by ikegami (Patriarch) on Nov 10, 2008 at 08:14 UTC
    For people that do error checking, it's not a problem.
    die if system("some_long_running_shell_command");

    Update: I had the condition negated, as pointed out in replies. Fixed.

      That may not always work; it depends what the return value of some_long_running_shell_command is when it receives a SIGINT. And it also depends what it returns when no SIGINT is given. "find" for instance returns (shell) FALSE on SIGINT, but also FALSE when it cannot process all files (for instance when the process doesn't have permission to decent into a directory). And (at least the 'find' found on my computer) it doesn't specify the actual exit values; just 0 and non-0.

      So, checking return values may work, but be aware, it will depend on the actual command run whether it will.

      For people that do error checking, it's not a problem. die if !system("some_long_running_shell_command");
      You mean die if system(...) as system returns 0 on success ... ok, this works for simple scripts, but in the general case you might want the script to handle the error instead of dying.

        Oops! So used to "if!" or "or" to catch errors. Fixed.

        As for the simplicity, it was just an example. You're right that it's really not suitable for use. The docs for system provide a more complete example.

      system returns TRUE on error so you are dieing if system("some_long_running_shell_command") executed successfully.

Re: system() requires double ctrl-c
by perlfan (Vicar) on Nov 10, 2008 at 19:45 UTC
    I am not trying to minimize your complaint, but anytime I've ever had to:
    #!/usr/local/bin/perl system("some_long_running_shell_command"); some_other_stuff();
    I just wrote a shell script that did "some_long_running_shell_command", then called a Perl script...but that's just me.