9 | 9 |
use warnings;
|
10 | 10 |
use Exporter ();
|
11 | 11 |
use File::Temp ();
|
12 | |
use IO::File;
|
13 | |
use IO::Handle;
|
14 | |
use IPC::Open3;
|
15 | |
use Symbol qw/qualify_to_ref/;
|
|
12 |
use IO::Handle ();
|
16 | 13 |
use Fatal qw/pipe open close/;
|
17 | 14 |
|
18 | 15 |
our $VERSION = '0.01';
|
|
45 | 42 |
}
|
46 | 43 |
|
47 | 44 |
#--------------------------------------------------------------------------#
|
48 | |
# _win32_wait_for_start
|
49 | |
#--------------------------------------------------------------------------#
|
50 | |
|
51 | |
sub _win32_wait_for_start {
|
52 | |
}
|
53 | |
# my @ ) = @_;
|
54 | |
## while (time - $now < 10) {
|
55 | |
## my ($out, $err) = _capture_tee(
|
56 | |
## sub { print STDOUT "test STDOUT\n"; print STDERR "test STDERR\n" }
|
57 | |
## );
|
58 | |
## $out ||= 'undef';
|
59 | |
## $err ||= 'undef';
|
60 | |
### print { $stderr } "Got '$out' and '$err'\n";
|
61 | |
## Win32::Sleep( 50 );
|
62 | |
## last if ($out eq "test STDOUT\n") && ($err eq "test STDERR\n");
|
63 | |
## }
|
64 | |
# # signal kid process ready to write to STDERR (the capture file)
|
65 | |
# kill 1, $_ for ($out_kid, $err_kid);
|
66 | |
# return;
|
67 | |
#}
|
68 | |
|
69 | |
#--------------------------------------------------------------------------#
|
70 | 45 |
# _capture_tee()
|
71 | |
#
|
72 | |
# kids for tee must always have cloned STD handle on STDOUT and capture
|
73 | |
# file on STDERR -- on Windows, need to ensure readiness before enabling
|
74 | |
# tee to capture_file on STDERR. See _win32_wait_for_start
|
75 | |
#--------------------------------------------------------------------------#
|
76 | |
|
77 | |
# command to tee output
|
|
46 |
#--------------------------------------------------------------------------#
|
|
47 |
|
|
48 |
# command to tee output on Win32 -- the argument is a filename that must
|
|
49 |
# be opened to signal that the process is ready to receive input.
|
|
50 |
# This is annoying, but seems to be the best that can be done on Win32
|
78 | 51 |
my @cmd = ($^X, '-e',
|
79 | |
'$SIG{HUP}=sub{exit}; my $f=shift; open my $flag, qq{>$f}; print {$flag} $$; close $flag; ' .
|
|
52 |
'$SIG{HUP}=sub{exit}; my $f=shift; open my $flag, qq{>$f}; ' .
|
|
53 |
'print {$flag} $$; close $flag; ' .
|
80 | 54 |
'my $buf; while (sysread(STDIN, $buf, 2048)) { ' .
|
81 | 55 |
'syswrite(STDOUT, $buf); syswrite(STDERR, $buf)}'
|
82 | 56 |
);
|
|
88 | 62 |
|
89 | 63 |
my $stdout_capture = File::Temp::tempfile();
|
90 | 64 |
my $stderr_capture = File::Temp::tempfile();
|
91 | |
# my $stdout_capture = IO::File->new( 'stdout.txt', '+>' );
|
92 | |
# my $stderr_capture = IO::File->new( 'stderr.txt', '+>' );
|
93 | 65 |
my ($stdout_tee, $stdout_reader, $stderr_tee, $stderr_reader) =
|
94 | 66 |
map { IO::Handle->new } 1 .. 4;
|
95 | 67 |
|
|
102 | 74 |
my @flag_files = map { scalar File::Temp::tmpnam() } 0 .. 1;
|
103 | 75 |
# start STDOUT listener
|
104 | 76 |
_open_std( $stdout_reader, $copy_of_std[1], $stdout_capture );
|
105 | |
my $out_kid = system(1, @cmd, $flag_files[0]);
|
|
77 |
push @pids, system(1, @cmd, $flag_files[0]);
|
106 | 78 |
push @tees, $stdout_tee;
|
107 | 79 |
# start STDERR listener
|
108 | 80 |
_open_std( $stderr_reader, $stderr_capture, $copy_of_std[2] );
|
109 | |
my $err_kid = system(1, @cmd, $flag_files[1]);
|
110 | |
push @pids, $out_kid, $err_kid;
|
|
81 |
push @pids, system(1, @cmd, $flag_files[1]);
|
111 | 82 |
push @tees, $stderr_tee;
|
|
83 |
# redirect our output to the subprocesses
|
|
84 |
_open_std( $copy_of_std[0], $stdout_tee, $stderr_tee );
|
112 | 85 |
# wait for the OS get the processes set up
|
113 | |
_open_std( $copy_of_std[0], $stdout_tee, $stderr_tee );
|
114 | 86 |
1 until -f $flag_files[0] && -f $flag_files[1];
|
115 | 87 |
unlink $_ for @flag_files;
|
116 | 88 |
}
|
|
121 | 93 |
$stdout_reader->close;
|
122 | 94 |
$stderr_reader->close;
|
123 | 95 |
}
|
124 | |
# otherwise redirect output to capture file
|
|
96 |
# if not teeing, redirect output to capture file
|
125 | 97 |
else {
|
126 | 98 |
_open_std( $copy_of_std[0], $stdout_capture, $stderr_capture );
|
127 | 99 |
}
|
128 | 100 |
|
129 | 101 |
# run code block
|
130 | 102 |
$code->();
|
131 | |
|
|
103 |
|
132 | 104 |
# restore original handles
|
133 | 105 |
_open_std( @copy_of_std );
|
134 | |
|
|
106 |
|
135 | 107 |
# shut down kids
|
136 | 108 |
if ( $tee ) {
|
137 | 109 |
close $_ for @tees; # they should stop when input closes
|
|
142 | 114 |
}
|
143 | 115 |
|
144 | 116 |
# read back capture output
|
145 | |
my ($got_out, $got_err) = map { seek $_, 0, 0; do {local $/; <$_>} }
|
146 | |
$stdout_capture, $stderr_capture;
|
|
117 |
my ($got_out, $got_err) =
|
|
118 |
map { seek $_,0,0; do {local $/; <$_>} } $stdout_capture, $stderr_capture;
|
147 | 119 |
|
148 | 120 |
return wantarray ? ($got_out, $got_err) : $got_out;
|
149 | 121 |
}
|
|
174 | 146 |
|
175 | 147 |
= NAME
|
176 | 148 |
|
177 | |
Capture::Tiny - Capture STDOUT and STDERR from perl, XS or system commands
|
|
149 |
Capture::Tiny - Capture STDOUT and STDERR from perl, XS or external programs
|
178 | 150 |
|
179 | 151 |
= VERSION
|
180 | 152 |
|
|
194 | 166 |
|
195 | 167 |
= DESCRIPTION
|
196 | 168 |
|
|
169 |
Capture::Tiny provides a simple, portable way to capture anything sent to
|
|
170 |
STDOUT or STDERR, regardless of whether it comes from Perl, from XS code or
|
|
171 |
from an external program. Optionally, output can be teed so that it is
|
|
172 |
captured while being passed through to the original handlers.
|
197 | 173 |
|
198 | 174 |
= USAGE
|
199 | 175 |
|
|
203 | 179 |
|
204 | 180 |
== tee
|
205 | 181 |
|
206 | |
($stdout, $stderr) = capture \&code_ref;
|
|
182 |
($stdout, $stderr) = tee \&code_ref;
|
207 | 183 |
|
208 | 184 |
= BUGS
|
209 | 185 |
|
|
216 | 192 |
|
217 | 193 |
= SEE ALSO
|
218 | 194 |
|
|
195 |
* [IO::CaptureOutput]
|
|
196 |
* [IPC::Cmd]
|
|
197 |
* [IPC::Open2]
|
|
198 |
* [IPC::Open3]
|
|
199 |
* [IPC::Run]
|
|
200 |
* [IPC::Run3]
|
219 | 201 |
|
220 | 202 |
= AUTHOR
|
221 | 203 |
|