Search code examples
perlwinapiwin32-process

How to redirect output of Win32::Process command to text file?


I am running a command in a Perl script using Win32::Process, and I need to redirect the output of that command to a text file. After doing a bit of research, this is what I am trying:

use Win32::Process;

open (OLDOUT, ">&STDOUT");
open (OLDERR, ">&STDERR");
my $file = "output.txt";
open (STDOUT, ">$file");
open (STDERR, ">&STDOUT");

my $timeout = 1000 * 60; # 60 second timeout
my $proc;
my $exit;
my $exe = "C:/Windows/System32/cmd.exe";
Win32::Process::Create($proc, $exe, "echo hello from process", 1, DETACHED_PROCESS, ".");
$proc->Wait($timeout);
$proc->GetExitCode($exit);

system("echo hello from system"); # To verify that the redirect is working

close (STDOUT);
close (STDERR);
open (STDOUT, ">&OLDOUT");
open (STDERR, ">&OLDERR");
close (OLDOUT);
close (OLDERR);

Unfortunately this is not working. In the output.txt file, I only get "hello from system". Is there a way to accomplish what I want using Win32::Process?

The reason I am using Win32::Process rather than backticks is because my command sometimes crashes, and I need to provide a timeout in order to kill it if neccessary. The ->Wait() function of Win32::Process allows me to do this.

I would rather have a solution using Win32::Process, as I am limited to which modules I have access to. However, if this really cannot be done, I would welcome an example solution using some other module.

Thank you.


Solution

  • You are specifying DETACHED_PROCESS when you start the process. The effect of this is the following:

    DETACHED_PROCESS 0x00000008

    For console processes, the new process does not inherit its parent's console (the default).

    See Process Creation Flags.

    The reason passing "echo hello from process" as the command line to Win32::Process doesn't work is because echo is a cmd.exe builtin. You need to instead use the command line 'cmd /c "echo hello from process"' as shown below:

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    use File::Which qw(which);
    use Win32;
    use Win32::Process;
    
    open OLDOUT, ">&STDOUT";
    open OLDERR, ">&STDERR";
    
    my $file = 'output.txt';
    
    open STDOUT, ">$file";
    open STDERR, ">&STDOUT";
    
    my $timeout = 15 * 1_000;
    my ($proc, $exit);
    my $exe = which 'cmd.exe';
    Win32::Process::Create($proc, $exe, 'cmd /c "echo hello from spawned process"', 1, 0, '.');
    $proc->Wait($timeout);
    $proc->GetExitCode($exit);
    
    print "Doing work ...\n"; sleep 3;
    
    print "Spawned process exited with $exit\n";
    
    close STDERR;
    close STDOUT;
    open STDERR, ">&OLDERR";
    open STDOUT, ">&OLDOUT";
    close OLDERR;
    close OLDOUT;
    

    Contents of output.txt:

    $ perl main.pl
    
    $ type output.txt
    hello from spawned process
    Doing work ...
    Spawned process exited with 0