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


In this script, a child process PMEMD will by executed.

system("PMEMD");

How do I get the process ID of it at the first place?
>br>Also, PMEMD will generate an output file which updates every couple seconds. What the script needs to do is (1)check the output file right after each update and (2)decide whether to kill the child process or not based on some data in the output.

I know how to open a file and read it and analyze the output but how does the script know when the output file gets updated? And how to kill the child process when certain criterion is satisfied?
  • Comment on How to monitor a child process from a perl script

Replies are listed 'Best First'.
Re: How to monitor a child process from a perl script
by Fletch (Bishop) on Aug 12, 2009 at 21:16 UTC

    You don't because system starts a synchronous child process (e.g. perl starts "PMEMD" and then waits for it to exit before continuing). If you want to create a separate simultaneous child process you need to either use bare fork and exec yourself or look into a module such as IPC::Run or the like.

    Update: And before someone else brings it up, yes on *NIX-ish OSen you can append an & to the command line you send to system and get the process backgrounded. But that's just asking for pain (especially since the OP wants the child's pid yadda yadda).

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: How to monitor a child process from a perl script
by BrowserUk (Patriarch) on Aug 12, 2009 at 21:29 UTC

    If the pmemd executable in question is this one, then you shoudl be able to ahieve your goal by redirecting the output back to your script through a pipe using the piped open.

    One Windows the syntax would be something like:

    my $pid = open KID, '-|', 'pmemd -O con' or die $!; while( <KID> ) { kill 'INT', $pid if m[some data]; }

    On *nix, you'd probably use something like tty (or pty?) instead of con to direct the output through the pipe.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Yes, you are right about the pmemd. However it has multiple output files. The whole command looks something like this:

      pmemd -i mdin -c inpcrd -p prmtop -r restrt -o mdout -x mdcrd

      restrt mdout and mdcrd are outputs while mdout is the one I am going to moniter changes.
      So how do you specify that very file in open?
        you are right about the pmemd. However it has multiple output files.

        But you only wish to monitor one of them, right?

        If so, then do as I suggested above, but with the extra options required. And if you need to retain the file (mdout) that would have been produced had you not redirected it, just write it yourself as you monitor the output.

        On Win*, it might look something like:

        my $pid = open KID, '-|', 'pmemd -i mdin -c inpcrd -p prmtop -r restrt -o con -x mdcrd' or die $!; open OUT, '>', 'mdout' or die $!; while( <KID> ) { kill 'INT', $pid if m[some data]; print OUT; }

        Again, I don't know the correct syntax for *nix. Maybe something like this would work?:

        my $pid = open KID, '-|', qq[pmemd -i mdin -c inpcrd -p prmtop -r restrt -o /dev/tty -x mdcr +d] or die $!; open OUT, '>', 'mdout' or die $!; while( <KID> ) { kill 'INT', $pid if m[some data]; print OUT; }

        Note the use of the list form of open to avoid a shell process.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: How to monitor a child process from a perl script
by JavaFan (Canon) on Aug 12, 2009 at 23:37 UTC
    How do I get the process ID of it at the first place?
    Use the return value of fork, and exec PMEMD in the child. system will only return after the process it spawn has finished.
    I know how to open a file and read it and analyze the output but how does the script know when the output file gets updated?
    You tell us. Unless PMEMD cooperates in some way, there's no way of knowing. Oh, you can inspect the last modification time of the file and/or its size and/or a checksum, but that's neither foolproof, nor can you know whether PMEMD is done writing, or whether it was PMEMD writing to the file in the first place. And PMEMD may buffer output. Without knowing how PMEMD operates, the question cannot be answered in a meaningful way. Perhaps just doing a tail -f of the file works. Perhaps you need to communicate with PMEMD using a shared memory semaphore.
    And how to kill the child process when certain criterion is satisfied?
    I guess kill() is a way too obvious way. Perhaps you should disconnect the power? Smash the CPU with a hammer?
      Man, you are funny!

      So can I do this:
      my $pid; if($pid=fork()) { open MDOUT "ele.mdout"; #some analysis about ele.mdout #.... #...... if(some criterion here) { kill $pid; } } else { exec("pmemd -i mdin -c inpcrd -r restrt -o ele.mdout"); }
        More or less. Good practises demand you test whether $pid is actually defined - if it's undefined, the fork failed. And you ought to check whether exec failed.
Re: How to monitor a child process from a perl script
by bichonfrise74 (Vicar) on Aug 12, 2009 at 21:25 UTC
    You might want to check out File::Monitor as well to find out if the file has changed.
Re: How to monitor a child process from a perl script
by ig (Vicar) on Aug 13, 2009 at 01:04 UTC

    If you only have one file to monitor and nothing else to do while you wait for it to be updated, you might find File::Tail helpful.

    update: Or, you could do something like the following:

    use strict; use warnings; open(my $fh, '<', 'test.dat') or die "test.dat: $!"; while(1) { my $line = <$fh>; if(defined($line)) { print "$line"; } else { sleep(10); } } close($fh);