TheAschr
TheAschr

Reputation: 973

Realtime backtick output processing

I don't know if this is possible but I'm working on sending the output of a command over sockets so it will be shown on both the client and server computer while the command is running. When I try to use backticks the output isn't saved into the variable until the command completes which is bad for when I run a command that takes a long time.

Question: Is there a way to scan the output on a line by line basis by say spawning another process and having it "watch" a variable?

Edit: This is what I tried with open

open DATA, "pause |" or die "Failed: $!";
while( defined(my $line = <DATA>)){
    chomp($line);
    print "$line\n";
}

I would like the output to be shown before the command completes.

Upvotes: 3

Views: 342

Answers (1)

ikegami
ikegami

Reputation: 385917

You have the right approach.

my @cmd = ( 'perl', '-e', '$|=1; for (1..5) { print "$_\n"; sleep 1; }' );

open(my $pipe, '-|', @cmd)
   or die("Can't launch child: $!\n");

while (defined( my $line = <$pipe> )) {
    chomp($line);
    print "<$line>\n";
}

When writing to STDOUT, most programs flush their output when a newline is encountered when STDOUT is a terminal ("line buffering"). Most programs fall back to block outputting chunks of 4 KiB or 8 KiB when STDOUT isn't a terminal ("block buffering"). That's why I had to use $|=1; in the child program in the example above.

Programs that behave as described can be fooled into using line buffering using pseudo-terminal instead of a pipe.

my @cmd = ( 'unbuffer', 'perl', '-e', 'for (1..5) { print "$_\n"; sleep 1; }' );

open(my $pipe, '-|', @cmd)
   or die("Can't launch child: $!\n");

while (defined( my $line = <$pipe> )) {
    chomp($line);
    print "<$line>\n";
}

IPC::Run provides a native method of creating pseudo-terminals.

use IPC::Run qw( run );

my @cmd = ( 'perl', '-e', 'for (1..5) { print "$_\n"; sleep 1; }' );

my $buf = '';
run(\@cmd, '>pty>', sub {
    $buf .= $_[0];
    while ($buf =~ s/^(.*)\r\n//) {
        print "<$1>\n";
    }
});

Upvotes: 6

Related Questions