Search code examples
perlterminalsignalskill-process

Perl fails to kill self pid when running from bash script


Following code behaves as expected when running from terminal:

perl -e 'kill -2, $$; warn HERE, $/'

It sends itself SIGINT and dies before reaching "HERE":

~# perl -e 'kill -2, $$; warn HERE, $/'

~# echo $?
130
~#

The problem: same code fails to kill self PID when running from shell script:

~# cat 1.sh
perl -e 'kill -2, $$; warn HERE, $/'
~#
~# sh 1.sh
HERE
~#
~# echo $?
0
~#

On the other hand, replacing perl's kill by a shell's one works OK:

~# cat 2.sh
perl -e 'qx/kill -2 $$/; warn HERE, $/'
~#
~# sh 2.sh
~#
~# echo $?
130
~#

Not really understand what is happening here, please help..


Solution

  • First of all,

     kill -2, $$
    

    is better written as

     kill 2, -$$
    

    An even better alternative is

     kill INT => -$$
    

    These send SIGINT to the specified process group.


    Your main question appears to be why the two shells behave differently. This section explains that.

    The process group represents an application.

    When you launch a program from an interactive shell, it's not part of a larger application, so the shell creates a new process group for the program.

    However, processes created by a script (i.e. a non-interactive shell) are part of the same application as the script itself, so the shell doesn't create a new process group for them.

    You can visualize this using the following:

    • sh -i <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\''' outputs the following:

      $ perl -e 'system ps => -o => "pid,ppid,pgrp,comm"'
        PID  PPID  PGRP COMMAND
       8179  8171  8179 bash
      14654  8179 14654 sh
      14655 14654 14655 perl
      14656 14655 14655 ps
      
      $ exit
      

      In interactive mode, perl is at the head of perl and ps's program group.

    • sh <<< 'perl -e '\''system ps => -o => "pid,ppid,pgrp,comm"'\''' outputs the following:

        PID  PPID  PGRP COMMAND
       8179  8171  8179 bash
      14584  8179 14584 sh
      14585 14584 14584 perl
      14586 14585 14584 ps
      

      In non-interactive mode, sh is at the head of perl and ps's program group.


    Your failures are the result of not sending the signal to the head of the process group (i.e. the application). Had you checked, the error kill reported was ESRCH ("No such process").

    ESRCH The pid or process group does not exist. [...]

    To kill the current process's process group, replace the improper

    kill INT => -$$           # XXX
    

    with

    kill INT => -getpgrp()    # Kill the application
    

    You can make your perl the head of its own process group by simply calling the following:

    setpgrp();
    

    Test:

    $ sh <<< 'perl -e '\''system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
      PID  PPID  PGRP COMMAND
     8179  8171  8179 bash
    16325  8179 16325 sh
    16326 16325 16325 perl
    16327 16326 16325 ps
    
    $ sh <<< 'perl -e '\''setpgrp(); system ps => ( -o => "pid,ppid,pgrp,comm" )'\'''
      PID  PPID  PGRP COMMAND
     8179  8171  8179 bash
    16349  8179 16349 sh
    16350 16349 16350 perl
    16351 16350 16350 ps
    

    That's not something you normally want to do.


    Finally, the Perl code

    kill INT => -$pgrp
    

    is equivalent to the following call of the kill command-line utility:

    kill -s INT -$pgrp
    kill -INT -$pgrp
    kill -2 -$pgrp
    

    You were missing - in your qx// program, so it was sending SIGINT to the identified process rather than the identified program group.