Home > Mobile >  Read from process before write with open3 in perl
Read from process before write with open3 in perl

Time:09-14

Let's say I have such a C program:

...
puts("What is your name?");
scanf("%s", name);
puts("Thank You!");
...

This program asks you to type your name, accepts input, displays "Thank You!" message and exits.

I want to write a program which automatically writes a name to my C program, receives output and prints it. Following program in perl works fine:

use strict;
use IPC::Open3;

my $bin = './myniceprogram';

my $pid = open3(\*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, "$bin")
    or die "open3() failed $!";

my $r;

print CHLD_IN "A"x10 . "\n";

$r = <CHLD_OUT>;
print "$r";

$r = <CHLD_OUT>;
print "$r";

waitpid $pid, 0;

It produces following output:

What is your name?
Thank You!

However, I would want to read the first line from my C program ("What is your name?") BEFORE writing to it. But if I change the order of read/writes in my perl program, it just hangs:

use strict;
use IPC::Open3;

my $bin = './myniceprogram';

my $pid = open3(\*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, "$bin")
    or die "open3() failed $!";

my $r;

$r = <CHLD_OUT>;
print "$r";

print CHLD_IN "A"x10 . "\n";

$r = <CHLD_OUT>;
print "$r";

waitpid $pid, 0;

With strace I can see that it's stuck on read call:

[myuser@myhost mydir]$ strace perl test2.pl
...
close(6)                                = 0
close(8)                                = 0
read(5,

But why?

CodePudding user response:

You are suffering from buffering. The child program is probably using the normal convention that STDOUT is line-buffered when connected to a terminal, but block-buffered otherwise.

If you can't, you can fool it into flushing its buffer when it outputs a line feed by using a pseudo-tty (ptty). A simple way of doing this is by executing unbuffer ./myniceprogram. IPC::Run also provides a simple mechanism for using pseudo-ttys. (Simply use <pty< and >pty> instead of < and >.)

But that's not going to work for binary data (data not organized into LF-terminated lines). A program that uses line-buffering for binary data should be considered buggy. If it's not meant to be interactive, it should use block buffering at all time. But this one is meant to be interactive, so it should flush its buffer at the appropriate time or avoid buffering entirely. Fixing this problem will require modifying myniceprogram.


Similarly, you probably want to disable buffering on CHLD_OUT. There are two ways of doing this:

Turn on autoflush for the handle:

CHLD_OUT->autoflush( 1 );

Flush for the handle after every write (print):

CHLD_OUT->flush();
  • Related