2

The following program spawns a new process which runs the windows command pause. The parent receives the child's stdout via a pipe and prints it out.

However, pause's output (Press any key to continue . . .) isn't being displayed until a newline is output. How can I resolve this?

use strict;
use warnings;

use Win32::Job;
use Time::Piece;
use POSIX qw( strftime );

$| = 1;

sub flush_handle {
    my ($fh) = @_;
    while (defined( my $line = <$fh> )) {
        chomp($line);
        print(">> $line\n");
    }
}

my $job = Win32::Job->new;

my $timeout = undef;
my $enable_stdout = 1;

pipe(my $in_pipe, my $out_pipe);
my $pre_term = 0;

my $pid = $job->spawn("cmd", "cmd /C \"pause\"", { stdout => $out_pipe });
close($out_pipe);

my $start_time = time();
my $pid_handler = $job->watch(
    sub {
        my ($h,$m,$s) = (gmtime(time()-$start_time))[2,1,0];
        if ($timeout && $s > $timeout) {
            close($in_pipe);
            $pre_term = 1;
            return 1;
        }

        if (!$enable_stdout) {
            print(sprintf("\r: - Elapsed Time: %02d:%02d:%02d",$h,$m,$s));
        } else {
            flush_handle($in_pipe);
        }

        return 0;           
    }
,1);
ikegami
  • 367,544
  • 15
  • 269
  • 518
TheAschr
  • 899
  • 2
  • 6
  • 19
  • The STDOUT of many programs is line-buffered (flushed on newline) when the program's STDOUT is connected to a terminal, and block-buffered (flushed when the file's buffer is full) otherwise (e.g. when it's connected to a pipe). /// `pause` does something similar. Its STDOUT isn't buffered when its connected to a terminal, and buffered otherwise. In other words, the problem is that `pause` isn't putting its output into the pipe until it exits. /// In unix, one would fool the child by using a pseudo-tty instead of a pipe (possibly using `unbuffer`). I don't know of any solutions for Windows. – ikegami Jan 02 '18 at 16:14
  • Off-topic bug 1: That's not a valid way of using `gmtime`. (You're passing an duration instead of a epoch time.) – ikegami Jan 02 '18 at 16:18
  • Off-topic bug 2: You should be comparing `time()-$start_time` against `$timeout`, not `$s`. – ikegami Jan 02 '18 at 16:19
  • Tip: `print(sprintf(...))` can be written as `printf(...)`. – ikegami Jan 02 '18 at 16:20
  • I'm using a custom function instead of print. When I run the above code, the elapsed time appears to be working but I haven't gotten up to an hour yet. The timeout is working correctly as well. The reason I changed your code originally was because $h wasn't defined. – TheAschr Jan 02 '18 at 16:28
  • The fact that your misuse of `gmtime` *appears* to work *now* is meaningless. /// No, it won't work with `$timeout >= 60` since `$s` will never be more than `59`. – ikegami Jan 02 '18 at 16:31
  • The `$h` in my code should have been `$s` – ikegami Jan 02 '18 at 16:36
  • Ah ok I see your point. Thanks! – TheAschr Jan 02 '18 at 16:37
  • You might be able to use the `winpty` program mentioned in https://stackoverflow.com/a/44531837/1334967 to trick `pause` into emitting its message immediately instead of buffering it – ottomeister Jan 03 '18 at 01:40

0 Answers0