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

I have a class with 2 methods: start() and finish(). The relevant parts are below.

(Updated for clarity)

package IPC_Class use strict; use warnings; sub new { shift; my $self = { 'cmds' => undef, @_ }; defined $self->{'cmds'} or die "ERROR: didn't get CMD in constructor +."; IPC::Shareable->clean_up_all; bless $self; return $self; } sub start { my $self = shift; my $fh_glob_ref = shift; ... # the cmd "grep 'zzz'" in the next line will fail. my $h = IPC::Run::start(['sort'], '<pipe', $fh_glob_ref, '|', ['grep +', 'zzz'], '>', $writefh, "2>", $writefh) or die "Error: $!\n"; $self->{'h'} = h; } sub finish { my $self = shift; $h = $self->{'h'}; if (!$h->finish()) { for ($h->full_results) { print "$_\n" }; } }
The idea is to encapsulate all the IPC work in that class. However, if I do this in the main program:
my $o = new IPC_Class; $o->start(\*FH_GLOB); # pass in a FH ref so we can pump more data to i +t later. # Now, add more data thru the FH. print FH_GLOB "line 1\n"; print FH_GLOB "line 2\n"; ... $o->finish();
Assuming I gave it a cmd that resulted in an exit code other than 0, $h->finish() should test false and $h->full_results should show the exit code for each cmd. This is not happening. Instead I get "unknown result, unknown PID" for each member of $h->full_results. Now, if I move the code in finish() to the main program, then it works as expected, i.e. $h->full_results does show the exit codes including the failed one. So for some reason, finish() doesn't seem to be getting the proper $h object. Would appreciate any pointer.

Replies are listed 'Best First'.
Re: IPC::Run harness question
by kcott (Archbishop) on May 03, 2014 at 06:05 UTC

    G'day italdesign,

    "So for some reason, finish() doesn't seem to be getting the proper $h object."

    The most likely reason is that you're using package global variables. If you move code from one package to another, the variables in that code will be global to the new package. Use lexical variables!

    Using package global variables will have other problems; for instance, in the code you show, you don't capture the object invoking your methods but instead just use whatever value $self happens to have.

    Furthermore, the code you have provided appears to have problems which strict would have alerted you to: 'vars' would tell you about "$self" and "$h" and 'subs' would tell you about "h". strict may find other problems in your complete code; warnings will alert you to other types of problems.

    To save yourself from another potential bug which can be hard to track down, I'd recommend you stop using the strongly discouraged Indirect Object Syntax.

    -- Ken

      Hi Ken, thanks for taking the time to reply. I'm actually not using package global variables. I should have included a few more lines:
      package IPC_Class use strict; use warnings; sub new { shift; my $self = { 'cmds' => undef, @_ }; defined $self->{'cmds'} or die "ERROR: didn't get CMD in constructor +."; IPC::Shareable->clean_up_all; bless $self; return $self; } sub start { my $self = shift; my $fh_glob_ref = shift; ... # the cmd "grep 'zzz'" in the next line will fail. my $h = IPC::Run::start(['sort'], '<pipe', $fh_glob_ref, '|', ['grep +', 'zzz'], '>', $writefh, "2>", $writefh) or die "Error: $!\n"; $self->{'h'} = h; } sub finish { my $self = shift; $h = $self->{'h'}; if (!$h->finish()) { for ($h->full_results) { print "$_\n" }; } }
      I'm really lost as to why it's not working :( The code in my start() method works beautifully. And if I move the code in my finish() method to the main program (having start() return $h), where I extract the data out of $h, then it works fine. But having the code in finish() and calling $o->finish() SOMEHOW loses the data in $h.

      (Thanks for the note on IOS. Lesson learned.)

        "I'm actually not using package global variables."

        Then you need to show the declaration of the lexical variables you are using.

        In:

        sub finish { ... $h = $self->{'h'}; ... }

        The scope of $h is not confined to &finish but it should be. You don't show where it's declared or what else uses this $h (with global-like scope). When you move the code in &finish to another package, a different global-like $h will be involved.

        In:

        sub start { ... $self->{'h'} = h; }

        I don't know what &h is doing: you don't show a sub h {...} definition. Maybe that's related to the problem. And, of course, h being a subroutine call is just a guess on my part: for all I know, it could be a directory handle.

        I recommend you reduce this down to the shortest amount of code which reproduces the problem ["How do I post a question effectively?" provides guidelines for achieving this]. Doing so, may actually highlight your problem such that you can resolve it yourself. If it doesn't, at least there's something we can run without having to guess what code you haven't shown.

        -- Ken