in reply to how to kill background process when script exit?

The simple way is not to involve the shell and launch your background process via open. Then you can kill your child at program end through kill:

my $kid = open(my $fh, "launch_java.sh |") or die "Couldn't launch 'launch_java.sh': $! / $?"; END { if ($kid) { kill 9 => $kid; # bang }; };

See perlipc for other methods.

Replies are listed 'Best First'.
Re^2: how to kill background process when script exit?
by Allasso (Monk) on Feb 19, 2011 at 22:30 UTC

    Am I missing a big pink elephant here?

    that does not seem to launch the process in the background though. I want to launch the java app, then continue on with my script (also, the pid isn't returned until I quit the java app - not real helpful). It seems that an '&' is necessary here, no?

    But anyway, I had already tried other things using open() as well, but I couldn't get them to work:

    $kid = open (FH, "java JavaApp | &"); print $kid;

    (gives "no such file or directory" message, $kid not returned until java app is quit)

    $kid = open (FH, "java JavaApp & |"); print $kid;

    (causes my script to hang, $kid not returned at all)

    for some reason, if I redirect the stderr to /dev/null:

    $kid = open (FH, "java JavaApp 2> /dev/null & |"); print $kid;

    the app DOES run in the background, $kid is returned immediately, HOWEVER, the app doesn't quit when I end the script. If I take the number returned in $kid (eg, 1001), and manually try to kill the process with it I get:

    kill 1001 -bash: kill: (1001) - No such process

    Also tried:

    `java JavaApp &`

    (gives "no such file or directory" message)

    ??????

    I have found lots of stuff on the net on how to start a background process in perl, but nothing on how to kill one (in a perl script).

      Alasso:

      I bet it's those ampersands that are getting in your way. The open statement, when given a string ending with a vertical bar tells perl to try to run the program in the background and redirect the standard output to the opened file stream. So in the first case, it fails because the string didn't end with a vertical bar, so it tried to use it as a filename. The second and third fail because when the shell starts, it then tries to open another process in the background. So you lose control of the child of the child.

      Give it a go without the ampersand, and let us know if that doesn't fix it.

      ...roboticus

      When your only tool is a hammer, all problems look like your thumb.

        robiticus

        okay, here's another example, with no ampersand. In this case, the app is (apparently) running in the background, because my script continues (prints out "pid: 1234"). HOWEVER, I can't exit the script until I exit the java app.

        #!/usr/bin/perl my($JAVA_CLASSPATH) = @ARGV; my $kid = open(my $fh, "java -classpath $JAVA_CLASSPATH TextEntry 2> / +dev/null |") or die "Couldn't launch 'launch_java.sh': $! / $?"; END { if ($kid) { kill 9 => $kid; # bang }; }; print "pid: $kid\n\npress return to exit script...\n\n"; <STDIN>;

      The problem is that $kid is the pid of the shell you launched (since you provided a shell command), and it exits as soon as it launches java. You could avoid the shell.

      use IPC::Open3 qw( open3 ); { open(local *CHILD_STDIN, '<', '/dev/null') or die; open(local *CHILD_STDERR, '>', '/dev/null') or die; local *FROM_CHILD; my $kid = open3('<CHILD_STDIN', \*FROM_CHILD, '>CHILD_STDERR', 'java', 'JavaApp' ); ... }

      Higher-level solutions possible using IPC::Run3 and IPC::Run.

        I just realized that what I had "discovered" is exactly what you were trying to tell me. Somehow I just wasn't seeing it. (wouldn't be the first time).

      First,

      $kid = open (FH, "java JavaApp 2> /dev/null & |");

      should be

      $kid = open (FH, "java JavaApp 2> /dev/null |");

      It makes no sense to use "&" there. Removing it might even solve your problem. $kid would still refer to the shell, but killing the shell might kill its children too. Killing the negative of the shell's PID has an even greater chance of working. But why keep the shell around?

      $kid = open (FH, "exec java JavaApp 2> /dev/null |");

        ikegami,

        I agree that it makes no sense to use it there, I was trying stuff out of desparation :-). It was interesting however the results that it did have, even though not what I ultimately want.

        It seems the consensus is that using open() to launch the app should run it in the background, but it just does not seem to be what I experience. My code (in the perl script) does not continue to execute until the child process has quit.

        I just caught your third example there - and it works also:
        $kid = open (FH, "exec java JavaApp 2> /dev/null |");
        So then instead of the shell running the app, the shell becomes the app, so the pid is the same. clever.

      Do all the redirection in the shell script and do not launch it in the background. The open(my $fh, "... |") will launch the script and also make it available for reading.

      As an alternative, just write the process id to a file and then exec the Java process from the shell script. If you use exec, the PID will remain the same. Then read that pid from Perl and kill that other process.

        Corion:

        "Do all the redirection in the shell script and do not launch it in the background. The open(my $fh, "... |") will launch the script and also make it available for reading."

        when I do this, I get basically the same results as opening the java app directly - my script continues, but I can't exit the script until I close the java app.

        also tried using the ampersand in the shell script, but then it doesn't kill the java app when I exit.

        I'm really trying, guys, maybe I'm missing something stupid.

      I found that redirecting output causes the process to be run in a shell (this is un-explicitly mentioned in some or all of the open[ 23]() docs), thus the pid returned is the pid of the shell process, not my command.

      ps aux | grep java | grep -v grep

      Revealed that it was killing the shell, but killing the shell did not stop the process from running.

      Corion was correct.

      ikegami tried to tell me about the shell thing, but I didn't catch what he was saying.

      here are some examples using open(), open2() and open3() and the results I observed:

      use IPC::Open2 qw( open2 ); use IPC::Open3 qw( open3 ); # this works: my $kid = open(FH1, "java -classpath $PATH TextEntry"); my $kid = open2(FH1, FH2, "java -classpath $PATH TextEntry"); my $kid = open3(FH1, FH2, FH3, "java -classpath $PATH TextEntry"); # however, when I try to suppress stderr, # it doesn't work because of the redirection, # and the process is launched it in a shell; # $kid is the pid of the shell: my $kid = open(FH1, "java -classpath $PATH TextEntry 2> /dev/null"); my $kid = open2(FH1, FH2, "java -classpath $PATH TextEntry 2> /dev/null"); # no problem, open3() will capture STDERR, taking # care of the redirection: my $kid = open3(FH1, FH2, FH3, "java -classpath $PATH TextEntry"); # however, the use IPC::Open2() docs # (to which IPC::Open3() is compared to) # indicates that the above form would be run in a shell, # so it may be safer to do this: my $kid = open3(FH1, FH2, FH3, "java", "-classpath", "$PATH", "TextEntry"); # but there is a way to capture the child output # using open() and still kill it when parent exits: # exec the command from the shell, thus # $kid = pid of shell = pid of the command # suggested by ikegami: my $kid = open(FH1, "exec java -classpath $PATH TextEntry 2> /dev/null");
Re^2: how to kill background process when script exit?
by Allasso (Monk) on Feb 19, 2011 at 23:02 UTC

    REALLY kludgy, but it works :-)...

    #!/usr/bin/perl my($JAVA_CLASSPATH) = @ARGV; $java_command = "java -classpath $JAVA_CLASSPATH TextEntry"; system("$java_command 2> /dev/null &"); $kid = `ps aux | grep "$java_command" | grep -v 'grep'`; $kid =~ s@^.*?\s+([0-9]+).*@$1@s; END { if ($kid) { kill 9 => $kid; # bang }; }; print "pid is: $pid\n\npress return to exit..."; <STDIN>

    I would really like to find a more elegant way however.

      You could use fork and exec instead. For example (no Unix box right now, so untested) something like:

      use strict; my $JAVA_CLASSPATH = '...whatever'; my $java_command = "java -classpath $JAVA_CLASSPATH TextEntry"; my $kid; defined($kid = fork()) or die "error: fork: $!"; if ($kid == 0) { ## child # Could redirect child stdout/stderr here if desired. # open(STDOUT, '>', 'javastdout.tmp') or die "redirect stdout: $ +!"; # open(STDERR, '>', 'javastderr.tmp') or die "redirect stderr: $ +!"; exec($java_command); die "error: exec: $!"; } warn "parent continues ... child pid is $kid\n"; # Could wait for child to exit using waitpid if required. # waitpid($kid, 0); # script continues... END { # Update: should first check that $kid is still alive before killi +ng it. # Something like: kill 0, $kid should do it (see "perldoc -f kill" +) if ($kid) { kill 9 => $kid; # bang }; };
      Also, as a matter of technique, you should try "kill 15" (SIGTERM) before resorting to "kill 9" so as to give the Java process a chance to catch the SIGTERM signal, clean up and die more gracefully.

      Update: For more elaborate sample code, using fork/exec, "kill 15" (softer kill before resorting to "kill 9"), "kill 0" (to check if process is still alive), and waitpid (to reap the exited child and test its exit code), see Timing and timing out Unix commands (especially the "kill_it" subroutine).

        Abbot:

        Just saw that, I'll have to look into it tomorrow, thanks.

        eyepopslikeamosquito:

        thanks, I had a chance to go through it, and it works fine. I was unfamiliar with fork() and exec() so I had to do a bit of studying first. I think open[_23]() provide a few more options and are more straightforward though, but maybe there is a price for this. Can you tell me any advantages to using fork()/exec() over open()?

        All in all, the experience has been very good for me. The whole thing about processes and threads has always been kind of mysterious to me, and I never have had a reason to grind through and get it figured out.

        I like your kill_it routine, and will use it.

      okay, this works (not sure if it is much more elegant, but...)

      perl script:

      #!/usr/bin/perl my($JAVA_CLASSPATH) = @ARGV; $LAUNCH_JAVA = $JAVA_CLASSPATH."/launch_java.sh"; open(FH, "$LAUNCH_JAVA $JAVA_CLASSPATH |") or die "Couldn't launch 'launch_java.sh': $! / $?"; my $kid = <FH>; $kid =~ s@\n@@; END { if ($kid) { kill 9 => $kid; # bang }; }; print "pid: $kid\n\npress return to exit script...\n\n"; <STDIN>;

      shell script (launch_java.sh):

      #/bin/bash java -classpath $1 TextEntry 2> /dev/null & echo $!
Re^2: how to kill background process when script exit?
by Allasso (Monk) on Feb 20, 2011 at 19:26 UTC
    please see update on my last long post about open(), open2(), open3(). Some serious mis-conclusions were corrected. Thank you Corion & ikegami