perlterminalipcopen3

How do I influence the width of Perl IPC::Open3 output?


I have the following Perl code and would like it to display exactly as invoking /bin/ls in the terminal would display. For example on a terminal sized to 100 columns, it would print up to 100 characters worth of output before inserting a newline. Instead this code prints 1 file per line of output. I feel like it involves assigning some terminal settings to the IO::Pty instance, but I've tried variations of that without luck.

UPDATE: I replaced the <$READER> with a call to sysread hoping the original code might just have a buffering issue, but the output received from sysread is still one file per line.

UPDATE: I added code showing my attempt at changing the IO::Pty's size via the clone_winsize_from method. This didn't result in the output being any different.

UPDATE: As best I can tell (from reading IPC::open3 code for version 1.12) it seems you cannot pass a variable of type IO::Handle without open3 creating a pipe rather than dup'ing the filehandle. This means isatty doesn't return a true value when ls invokes it and ls then forces itself into "one file per line" mode.

I think I just need to do a fork/exec and handle the I/O redirection myself.

#!/usr/bin/env perl
use IPC::Open3;
use IO::Pty;
use strict;

my $READER = IO::Pty->new();
$READER->slave->clone_winsize_from(\*STDIN);

my $pid = open3(undef, $READER, undef, "/bin/ls");

while(my $line = <$READER>)
{
    print $line;
}
waitpid($pid, 0) or die "Error waiting for pid: $!\n";

$READER->close();

Solution

  • I think $READER is getting overwritten with a pipe created by open3, which can be avoided by changing

    my $READER = ...;
    my $pid = open3(undef, $READER, undef, "/bin/ls");
    

    to

    local *READER = ...;
    my $pid = open3(undef, '>&READER', undef, "/bin/ls");
    

    See the docs.