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

Hello everyone, I make a quick Perl utility that'll launch 2 different applications asynchronously using fork. The problem is that the Perl app does not exit once the 2 external apps were opened. I tried 'exit' but aren't succesful. The Perl app exits only after both external apps were closed. Is there a way I can make the Perl app disappear once it did its job of launching 2 external apps ? Thanks very much.

Example

unless (defined($child_pid=fork())) {die "Can not fork.\n"}; if ($child_pid) { # parent system "$app1" || die "Could not launch target\n"; exit; } else { # child system "$app2" || die "Could not launch target\n"; exit; }

Replies are listed 'Best First'.
Re: launch and then exit out of app
by Zaxo (Archbishop) on Feb 03, 2004 at 16:41 UTC

    You could call exec instead. Actually, the parent only hangs around until its own system call is done. If the child is hanging around, too, it may have gone zombie. Dealing with external apps, you probably will need to set $SIG{CHLD} = 'IGNORE'; (if supported) to avoid the Z's.

    Added: to launch some external utilities and exit,

    my @apps = ( [qw(/path/to/foo args of foo)], [qw(/path/to/bar args of it here)] ); $SIG{CHLD} = 'IGNORE'; for (@apps) { defined(my $cpid = fork) or die $!; $cpid or exec {$_->[0]} @$_ or die $!; } exit 0; # the parent
    If the apps don't run as daemons, you may need to have them ignore SIGHUP or else call &POSIX::setsid so that the parent's exit doesn't trigger an early demise of the kids.

    After Compline,
    Zaxo

Re: launch and then exit out of app
by blue_cowdawg (Monsignor) on Feb 03, 2004 at 16:55 UTC

    Check this out AM:

    my $child=fork(); # Spawn off a child if ($child>0) { #parent exec ($app1) || die "Could not exec $app1:$!"; exit(0); # Should never get here! } elsif ($child == 0 ) { # Child process exec ($app2) || die "Could not exec $app2:$!"; exit(0); # should never get here either } else { die "Could not fork! $!"; }
    For doing spawning and dying parents I much prefer the mehodology of using an exec instead of system. Evaluating $! will give you the reason you couldn't spawn the exec'ed process.

    As always in Perl TIMTOWTDI and YMMV. One thing you didn't specify is what platform you are attempting this feat of derring do...


    Peter L. Berghold -- Unix Professional
    Peter at Berghold dot Net
       Dog trainer, dog agility exhibitor, brewer of fine Belgian style ales. Happiness is a warm, tired, contented dog curled up at your side and a good Belgian ale in your chalice.
      Hi, I run it on Windows 2000. I tried what you suggested by using 'exec' instead of 'system', but it only launches 1 app and then exit !?!

            I run it on Windows 2000.

        Hence my comment about your not specifying which OS you were attempting this on. It is known that fork() doesn't always play nice on Windows.

        Suggest you look at the solution proposed by brer flyingmoose here. In order to do the exit that you ask for replace his line that says $proc->wait(); with exit(0); and you should be OK.

        DISCLAIMER: Don't have a Windows 2000 box to test this on so do your own testing


        Peter L. Berghold -- Unix Professional
        Peter at Berghold dot Net
           Dog trainer, dog agility exhibitor, brewer of fine Belgian style ales. Happiness is a warm, tired, contented dog curled up at your side and a good Belgian ale in your chalice.

        I run it on Windows 2000.

        Then you could use the Win32::Job module, standard with ActivePerl 5.8. For example:

        use strict; use Win32::Job; my @cmds = ( [ "netstat", 'netstat -na' ], [ $^X, 'perl -le "print\"pid=$$:Sleep 5 secs.\";sleep 5"' ], ); my $job = Win32::Job->new(); my @pids; my $i = 0; for my $cmd (@cmds) { ++$i; my $pid = $job->spawn($cmd->[0], $cmd->[1], { stdin => 'NUL', stdout => "$i.out", stderr => "$i.err" } ) or die "spawn: $^E"; push(@pids, $pid); } print "Processes pids: @pids are running...\n"; $job->run(60); # allow up to 60 seconds for all processes to end print "Job complete (output in files 1.out/1.err/2.out/2.err).\n"; my $stat = $job->status(); for my $pid (@pids) { exists($stat->{$pid}) or die "oops, no status for $pid"; my $rc = $stat->{$pid}->{exitcode}; my $t = $stat->{$pid}->{time}; print "pid=$pid, rc=$rc, elapsed time=$t->{elapsed} secs\n"; }

        Update: Sorry, on re-reading your question, I don't think Win32::Jobs will help you. I suggest you simply launch the processes using Win32::Process (also standard with ActivePerl) and exit.

        use strict; use Win32::Process; my $SysDir = "$ENV{SystemRoot}\\system32"; my @cmds = ( [ "$SysDir\\netstat.exe", 'netstat -na' ], [ $^X, 'perl -le "print\"pid=$$:Sleep 5 secs.\";sleep 5"' ], ); for my $cmd (@cmds) { Win32::Process::Create(my $hProc, # process object $cmd->[0], # executable $cmd->[1], # command line 1, # inherit handles NORMAL_PRIORITY_CLASS, # priority '.') # working dir or die "error create process: $^E\n"; my $pid = $hProc->GetProcessID(); print "Process pid $pid launched.\n"; } print "I'm outta here.\n";
Re: launch and then exit out of app
by flyingmoose (Priest) on Feb 03, 2004 at 18:58 UTC
    I have used Proc::Background a few times and it is much more readable than fork and exec. Plus, it works on Windows too -- fork tends not to work well there.

    use Proc::Background; my $proc; my @commands=($cmd1,$cmd2); foreach my $foo (@commands) { $proc = Proc::Background->new($foo); }

    (I have updated the code above to remove the wait call, note: Proc::Background has some very good features if you want to wait on a process or check whether processes are still running...those features are not required here)

      Good tip. Note that fork (the OS resource) doesn't exists on Win32. Soo, the fork() function on Win32 just create a new thread, what doesn't change the proccess ID, what shows to us that the approach of fork() on Win32 does nothing.

      Graciliano M. P.
      "Creativity is the expression of the liberty".