gizeh
gizeh

Reputation: 167

IPC between two Perl programs

I have a Perl program(this is the PARENT) which needs variables from another Perl prog(CHILD). I want to realize that with the pipe mechanism of Linux. After unsuccessfull search in net (none of the numerous examples fit this basic theme -- I think the concept is quite understandable for a nonexpert, but I cannot implement a running example). The two appended progs show my understanding of piping, which is probably totally wrong, but I want to learn it. For clarity:

Prog PARENT is running
    needs 2 variables from Prog CHILD
    PARENT calls CHILD (open CHILD ... ?)
    CHILD is running and can deliver the 2 variables
    CHILD opens PARENT, write/print the variables to PARENT
    CHILD closes PARENT
    CHILD exit
PARENT can now read from CHILD 

The PARENT Program (Caller and Receiver)

#!/usr/bin/env perl
use strict;
use warnings;
# file-name: mwe-ipc.pl
# this prog. is the PARENT
# Calls a CHILD by its prog.name

print "$0\n"; # show your progname
my $pid = open(CHILD, "mwe-ipc-child.pl |") or die "Couldn't fork: $!\n";
my @arr_receiver;
while (<CHILD>){
    # PARENT needs two variables from CHILD
    # how to get var1 and var2?
   @arr_receiver = $_;
}
close(CHILD);

print "arr_receiver[$_] = $arr_receiver[$_]\n" for (0..$#arr_receiver);

The CHILD Program (will be called and answers)

#!/usr/bin/env perl
use strict;
use warnings;
# file-name: mwe-ipc-child.pl
# this prog. is the CHILD
# Called from a PARENT

print "$0\n";;
my $pid = open(PARENT, "| mwe-ipc.pl") or die "Couldn't fork: $!\n";
my $var1 = "a"; #"|l |p{2.7cm} |p{2cm}";
my $var2 = "b"; #"\textbf{G}& \textbf{Substantiv}& \textbf{Modus} \\";

while (<PARENT>){
   # PARENT needs two variables from CHILD
   # how to put var1 and var2?
   print PARENT $var1, $var2;
}
close(PARENT);

exit(0);

Call to PARENT Prog outputs progname, then endless loop. Ultimately needless call from cmdline of CHILD delivers own and PARENTS' progname. Can someone please help?

Upvotes: 2

Views: 108

Answers (2)

gizeh
gizeh

Reputation: 167

Many, many Thanks for your answer. Your solution works as it is. But for those who are similar interested as me I want to show following adaption of your source which should fit exactly the original question. Interchange variables from a child to parent. Important additional knowledge is here

1) do{local... in: https://www.perlmonks.org/?node_id=287647

2) no suffer from buffer in: Why doesn't my parent process see the child's output until it exits?

The parent:

#!/usr/bin/env perl
use strict;
use warnings;
# file-name: mwe-ipc.pl
# this prog. is the PARENT
# Calls a CHILD by its prog.name

print "$0\n";
use utf8;
use open ':std', ':encoding(UTF-8)';

open(my $CHILD, "-|", "mwe-ipc-child.pl") or die("Can't execute child: $!\n");

# do { local $/; <$CHILD> }; this idiom is a consise way to slurp the entire 
# contents of a file into a scalar without using a loop
# $/ is the inut record separator(irs). In following manner the same as 
# local $/ = undef. This eliminates the irs to nothing, which leads to a slurp 
# instead of reacting upon \n, the default value, the new line sign. 
# Local restricts that behaviour to the block {}.
my $response = do { local $/; <$CHILD> };

close($CHILD);

#... do something with response...
print "response from child is : $response \n";
my @arr_response = split /\n/, $response;
print "arr_response[$_] = $arr_response[$_]\n" for (0..$#arr_response);

The Child:

#!/usr/bin/env perl
use strict;
use warnings;
# file-name: mwe-ipc-child.pl
# this prog. is the CHILD
# Called from a PARENT

#print "$0\n"; # uncommented this print goes to the PARENT too.
use open ':std', ':encoding(UTF-8)';
my $var1 = "|l |p{2.7cm} |p{2cm}";
my $var2 = "\\textbf{G}& \\textbf{Substantiv}& \\textbf{Modus} \\\\";
my @response;

print ($var1,"\n", $var2,"\n");

The call of "mwe-ipc.pl" leads to

/home/hj/latex/mwes/ipc/mwe-ipc.pl
response from child is : |l |p{2.7cm} |p{2cm}
\textbf{G}& \textbf{Substantiv}& \textbf{Modus} \\

and

arr_response[0] = |l |p{2.7cm} |p{2cm}
arr_response[1] = \textbf{G}& \textbf{Substantiv}& \textbf{Modus} \\

So, exactly what I wanted, thanks @ikegami

Upvotes: 0

ikegami
ikegami

Reputation: 385857

Problem #1: Fundamentals

Having the child execute the parent makes no sense. The output to be captured by the parent should be sent to STDOUT.

The starting skeletons would look as follows:

  • Parent:

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    use utf8;
    use open ':std', ':encoding(UTF-8)';
    
    open(my $CHILD, "-|", "mwe-ipc-child.pl")
       or die("Can't execute child: $!\n");
    
    my $response = do { local $/; <$CHILD> };
    close($CHILD);
    
    ... do something with response...
    
  • Child:

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    use utf8;
    use open ':std', ':encoding(UTF-8)';
    
    my $response = ...;
    print($repsonse);
    

Problem #2: Protocol

You need some kind of way of telling where one value ends and the other starts.

If the values will never contain a line feed, you could separate them using a line feed.

  • Child:

    print("$var1\n$var2\n");
    
  • Parent:

    chomp( my $var1 = <$CHILD> );
    chomp( my $var2 = <$CHILD> );
    

Otherwise, a nice extensible method would be to use JSON.

  • Child:

    use Cpanel::JSON::XS qw( encode_json );
    
    my $data = { var1 => $var1, var2 => $var2 };
    my $json = encode_json($data);
    binmode(STDOUT);
    print($json);
    
  • Parent:

    use Cpanel::JSON::XS qw( decode_json );
    
    open(my $CHILD, "-|:raw", "mwe-ipc-child.pl") or ...;
    my $json = do { local $/; <$CHILD> };
    close($CHILD);
    
    my $data = decode_json($json);
    my $var1 = $data->{var1};
    my $var2 = $data->{var2};
    

Upvotes: 4

Related Questions