| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
916
|
|
|
916
|
|
36285554
|
use 5.006; |
|
|
916
|
|
|
|
|
4038
|
|
|
|
916
|
|
|
|
|
37506
|
|
|
2
|
916
|
|
|
916
|
|
4767
|
use strict; |
|
|
916
|
|
|
|
|
1744
|
|
|
|
916
|
|
|
|
|
24715
|
|
|
3
|
916
|
|
|
916
|
|
4763
|
use warnings; |
|
|
916
|
|
|
|
|
1470
|
|
|
|
916
|
|
|
|
|
50491
|
|
|
4
|
|
|
|
|
|
|
package Capture::Tiny::Extended; |
|
5
|
|
|
|
|
|
|
our $VERSION = '0.114'; # VERSION |
|
6
|
|
|
|
|
|
|
# ABSTRACT: Capture STDOUT and STDERR from from Perl, XS or external programs (with some extras) |
|
7
|
916
|
|
|
916
|
|
5033
|
use Carp (); |
|
|
916
|
|
|
|
|
1463
|
|
|
|
916
|
|
|
|
|
16551
|
|
|
8
|
916
|
|
|
916
|
|
4666
|
use Exporter (); |
|
|
916
|
|
|
|
|
1735
|
|
|
|
916
|
|
|
|
|
11809
|
|
|
9
|
916
|
|
|
916
|
|
5386039
|
use IO::Handle (); |
|
|
916
|
|
|
|
|
8837305
|
|
|
|
916
|
|
|
|
|
20602
|
|
|
10
|
916
|
|
|
916
|
|
7960
|
use File::Spec (); |
|
|
916
|
|
|
|
|
1742
|
|
|
|
916
|
|
|
|
|
18491
|
|
|
11
|
916
|
|
|
916
|
|
2171910
|
use File::Temp qw/tempfile tmpnam/; |
|
|
916
|
|
|
|
|
25248161
|
|
|
|
916
|
|
|
|
|
127049
|
|
|
12
|
|
|
|
|
|
|
# Get PerlIO or fake it |
|
13
|
|
|
|
|
|
|
BEGIN { |
|
14
|
916
|
|
|
916
|
|
2198
|
local $@; |
|
15
|
916
|
|
|
|
|
8151
|
eval { require PerlIO; PerlIO->can('get_layers') } |
|
|
916
|
|
|
|
|
473112
|
|
|
16
|
916
|
50
|
|
|
|
2197
|
or *PerlIO::get_layers = sub { return () }; |
|
|
0
|
|
|
|
|
0
|
|
|
17
|
|
|
|
|
|
|
} |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our @ISA = qw/Exporter/; |
|
20
|
|
|
|
|
|
|
our @EXPORT_OK = qw/capture capture_merged tee tee_merged capture_files/; |
|
21
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK ); |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
my $IS_WIN32 = $^O eq 'MSWin32'; |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
our $DEBUG = $ENV{PERL_CAPTURE_TINY_DEBUG}; |
|
26
|
|
|
|
|
|
|
my $DEBUGFH; |
|
27
|
|
|
|
|
|
|
open $DEBUGFH, ">&STDERR" if $DEBUG; |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
*_debug = $DEBUG ? sub(@) { print {$DEBUGFH} @_ } : sub(){0}; |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our $TIMEOUT = 30; |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
34
|
|
|
|
|
|
|
# command to tee output -- the argument is a filename that must |
|
35
|
|
|
|
|
|
|
# be opened to signal that the process is ready to receive input. |
|
36
|
|
|
|
|
|
|
# This is annoying, but seems to be the best that can be done |
|
37
|
|
|
|
|
|
|
# as a simple, portable IPC technique |
|
38
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
39
|
|
|
|
|
|
|
my @cmd = ($^X, '-e', '$SIG{HUP}=sub{exit}; ' |
|
40
|
|
|
|
|
|
|
. 'if( my $fn=shift ){ open my $fh, qq{>$fn}; print {$fh} $$; close $fh;} ' |
|
41
|
|
|
|
|
|
|
. 'my $buf; while (sysread(STDIN, $buf, 2048)) { ' |
|
42
|
|
|
|
|
|
|
. 'syswrite(STDOUT, $buf); syswrite(STDERR, $buf)}' |
|
43
|
|
|
|
|
|
|
); |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
46
|
|
|
|
|
|
|
# filehandle manipulation |
|
47
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub _relayer { |
|
50
|
315208
|
|
|
315208
|
|
856752
|
my ($fh, $layers) = @_; |
|
51
|
315208
|
|
|
|
|
1040827
|
_debug("# requested layers (@{$layers}) to $fh\n"); |
|
|
315208
|
|
|
|
|
4083962
|
|
|
52
|
315208
|
|
|
|
|
4820113
|
my %seen = ( unix => 1, perlio => 1 ); # filter these out |
|
53
|
315208
|
|
|
|
|
869431
|
my @unique = grep { !$seen{$_}++ } @$layers; |
|
|
768506
|
|
|
|
|
2847506
|
|
|
54
|
315208
|
|
|
|
|
1721780
|
_debug("# applying unique layers (@unique) to $fh\n"); |
|
55
|
315208
|
|
|
|
|
4664490
|
binmode($fh, join(":", ":raw", @unique)); |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub _name { |
|
59
|
2253248
|
|
|
2253248
|
|
3920691
|
my $glob = shift; |
|
60
|
916
|
|
|
916
|
|
5676
|
no strict 'refs'; ## no critic |
|
|
916
|
|
|
|
|
1921
|
|
|
|
916
|
|
|
|
|
4883128
|
|
|
61
|
2253248
|
|
|
|
|
2710630
|
return *{$glob}{NAME}; |
|
|
2253248
|
|
|
|
|
20194731
|
|
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub _open { |
|
65
|
891969
|
50
|
|
891969
|
|
59270499
|
open $_[0], $_[1] or Carp::confess "Error from open(" . join(q{, }, @_) . "): $!"; |
|
66
|
891969
|
50
|
|
|
|
3240722
|
_debug( "# open " . join( ", " , map { defined $_ ? _name($_) : 'undef' } @_ ) . " as " . fileno( $_[0] ) . "\n" ); |
|
|
1783938
|
|
|
|
|
5689195
|
|
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub _close { |
|
70
|
385600
|
50
|
|
385600
|
|
9689422
|
close $_[0] or Carp::confess "Error from close(" . join(q{, }, @_) . "): $!"; |
|
71
|
385600
|
50
|
|
|
|
1508048
|
_debug( "# closed " . ( defined $_[0] ? _name($_[0]) : 'undef' ) . "\n" ); |
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
my %dup; # cache this so STDIN stays fd0 |
|
75
|
|
|
|
|
|
|
my %proxy_count; |
|
76
|
|
|
|
|
|
|
sub _proxy_std { |
|
77
|
88390
|
|
|
88390
|
|
168356
|
my %proxies; |
|
78
|
88390
|
100
|
|
|
|
307666
|
if ( ! defined fileno STDIN ) { |
|
79
|
16443
|
|
|
|
|
31596
|
$proxy_count{stdin}++; |
|
80
|
16443
|
100
|
|
|
|
49082
|
if (defined $dup{stdin}) { |
|
81
|
2205
|
|
|
|
|
11930
|
_open \*STDIN, "<&=" . fileno($dup{stdin}); |
|
82
|
2205
|
50
|
|
|
|
12389
|
_debug( "# restored proxy STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
else { |
|
85
|
14238
|
|
|
|
|
394599
|
_open \*STDIN, "<" . File::Spec->devnull; |
|
86
|
14238
|
50
|
|
|
|
101268
|
_debug( "# proxied STDIN as " . (defined fileno STDIN ? fileno STDIN : 'undef' ) . "\n" ); |
|
87
|
14238
|
|
|
|
|
241064
|
_open $dup{stdin} = IO::Handle->new, "<&=STDIN"; |
|
88
|
|
|
|
|
|
|
} |
|
89
|
16443
|
|
|
|
|
54216
|
$proxies{stdin} = \*STDIN; |
|
90
|
16443
|
50
|
|
|
|
144443
|
binmode(STDIN, ':utf8') if $] >= 5.008; |
|
91
|
|
|
|
|
|
|
} |
|
92
|
88390
|
100
|
|
|
|
397842
|
if ( ! defined fileno STDOUT ) { |
|
93
|
14238
|
|
|
|
|
37503
|
$proxy_count{stdout}++; |
|
94
|
14238
|
50
|
|
|
|
59762
|
if (defined $dup{stdout}) { |
|
95
|
0
|
|
|
|
|
0
|
_open \*STDOUT, ">&=" . fileno($dup{stdout}); |
|
96
|
0
|
0
|
|
|
|
0
|
_debug( "# restored proxy STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
else { |
|
99
|
14238
|
|
|
|
|
366258
|
_open \*STDOUT, ">" . File::Spec->devnull; |
|
100
|
14238
|
50
|
|
|
|
128708
|
_debug( "# proxied STDOUT as " . (defined fileno STDOUT ? fileno STDOUT : 'undef' ) . "\n" ); |
|
101
|
14238
|
|
|
|
|
271347
|
_open $dup{stdout} = IO::Handle->new, ">&=STDOUT"; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
14238
|
|
|
|
|
47732
|
$proxies{stdout} = \*STDOUT; |
|
104
|
14238
|
50
|
|
|
|
126567
|
binmode(STDOUT, ':utf8') if $] >= 5.008; |
|
105
|
|
|
|
|
|
|
} |
|
106
|
88390
|
100
|
|
|
|
277135
|
if ( ! defined fileno STDERR ) { |
|
107
|
14238
|
|
|
|
|
33649
|
$proxy_count{stderr}++; |
|
108
|
14238
|
50
|
|
|
|
46787
|
if (defined $dup{stderr}) { |
|
109
|
0
|
|
|
|
|
0
|
_open \*STDERR, ">&=" . fileno($dup{stderr}); |
|
110
|
0
|
0
|
|
|
|
0
|
_debug( "# restored proxy STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
else { |
|
113
|
14238
|
|
|
|
|
364169
|
_open \*STDERR, ">" . File::Spec->devnull; |
|
114
|
14238
|
50
|
|
|
|
78779
|
_debug( "# proxied STDERR as " . (defined fileno STDERR ? fileno STDERR : 'undef' ) . "\n" ); |
|
115
|
14238
|
|
|
|
|
408914
|
_open $dup{stderr} = IO::Handle->new, ">&=STDERR"; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
14238
|
|
|
|
|
46681
|
$proxies{stderr} = \*STDERR; |
|
118
|
14238
|
50
|
|
|
|
172997
|
binmode(STDERR, ':utf8') if $] >= 5.008; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
88390
|
|
|
|
|
369696
|
return %proxies; |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub _unproxy { |
|
124
|
86586
|
|
|
86586
|
|
463196
|
my (%proxies) = @_; |
|
125
|
86586
|
|
|
|
|
521933
|
_debug( "# unproxing " . join(" ", keys %proxies) . "\n" ); |
|
126
|
86586
|
|
|
|
|
577005
|
for my $p ( keys %proxies ) { |
|
127
|
44289
|
|
|
|
|
120024
|
$proxy_count{$p}--; |
|
128
|
44289
|
|
|
|
|
270978
|
_debug( "# unproxied " . uc($p) . " ($proxy_count{$p} left)\n" ); |
|
129
|
44289
|
100
|
|
|
|
162330
|
if ( ! $proxy_count{$p} ) { |
|
130
|
42174
|
|
|
|
|
133895
|
_close $proxies{$p}; |
|
131
|
42174
|
50
|
|
|
|
280836
|
_close $dup{$p} unless $] < 5.008; # 5.6 will have already closed this as dup |
|
132
|
42174
|
|
|
|
|
1241657
|
delete $dup{$p}; |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub _copy_std { |
|
138
|
88390
|
|
|
88390
|
|
183628
|
my %handles = map { $_, IO::Handle->new } qw/stdin stdout stderr/; |
|
|
265170
|
|
|
|
|
6340855
|
|
|
139
|
88390
|
|
|
|
|
2731829
|
_debug( "# copying std handles ...\n" ); |
|
140
|
88390
|
|
|
|
|
260794
|
_open $handles{stdin}, "<&STDIN"; |
|
141
|
88390
|
|
|
|
|
272441
|
_open $handles{stdout}, ">&STDOUT"; |
|
142
|
88390
|
|
|
|
|
412507
|
_open $handles{stderr}, ">&STDERR"; |
|
143
|
88390
|
|
|
|
|
416682
|
return \%handles; |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub _open_std { |
|
147
|
174976
|
|
|
174976
|
|
515640
|
my ($handles) = @_; |
|
148
|
174976
|
|
|
|
|
2654605
|
_open \*STDIN, "<&" . fileno $handles->{stdin}; |
|
149
|
174976
|
|
|
|
|
1331450
|
_open \*STDOUT, ">&" . fileno $handles->{stdout}; |
|
150
|
174976
|
|
|
|
|
981929
|
_open \*STDERR, ">&" . fileno $handles->{stderr}; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
154
|
|
|
|
|
|
|
# private subs |
|
155
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub _start_tee { |
|
158
|
41855
|
|
|
41855
|
|
328203
|
my ($which, $stash) = @_; |
|
159
|
|
|
|
|
|
|
# setup pipes |
|
160
|
41855
|
|
|
|
|
2136454
|
$stash->{$_}{$which} = IO::Handle->new for qw/tee reader/; |
|
161
|
41855
|
|
|
|
|
11094047
|
pipe $stash->{reader}{$which}, $stash->{tee}{$which}; |
|
162
|
41855
|
|
|
|
|
921857
|
_debug( "# pipe for $which\: " . _name($stash->{tee}{$which}) . " " |
|
163
|
|
|
|
|
|
|
. fileno( $stash->{tee}{$which} ) . " => " . _name($stash->{reader}{$which}) |
|
164
|
|
|
|
|
|
|
. " " . fileno( $stash->{reader}{$which}) . "\n" ); |
|
165
|
41855
|
|
|
|
|
1361656
|
select((select($stash->{tee}{$which}), $|=1)[0]); # autoflush |
|
166
|
|
|
|
|
|
|
# setup desired redirection for parent and child |
|
167
|
41855
|
|
|
|
|
191986
|
$stash->{new}{$which} = $stash->{tee}{$which}; |
|
168
|
41855
|
|
|
|
|
434068
|
$stash->{child}{$which} = { |
|
169
|
|
|
|
|
|
|
stdin => $stash->{reader}{$which}, |
|
170
|
|
|
|
|
|
|
stdout => $stash->{old}{$which}, |
|
171
|
|
|
|
|
|
|
stderr => $stash->{capture}{$which}, |
|
172
|
|
|
|
|
|
|
}; |
|
173
|
|
|
|
|
|
|
# flag file is used to signal the child is ready |
|
174
|
41855
|
|
|
|
|
1903988
|
$stash->{flag_files}{$which} = scalar tmpnam(); |
|
175
|
|
|
|
|
|
|
# execute @cmd as a separate process |
|
176
|
41855
|
50
|
|
|
|
29662314
|
if ( $IS_WIN32 ) { |
|
177
|
0
|
|
|
|
|
0
|
local $@; |
|
178
|
0
|
|
|
|
|
0
|
eval "use Win32API::File qw/CloseHandle GetOsFHandle SetHandleInformation fileLastError HANDLE_FLAG_INHERIT INVALID_HANDLE_VALUE/ "; |
|
179
|
0
|
0
|
|
|
|
0
|
_debug( "# Win32API::File loaded\n") unless $@; |
|
180
|
0
|
|
|
|
|
0
|
my $os_fhandle = GetOsFHandle( $stash->{tee}{$which} ); |
|
181
|
0
|
0
|
0
|
|
|
0
|
_debug( "# Couldn't get OS handle: " . fileLastError() . "\n") if ! defined $os_fhandle || $os_fhandle == INVALID_HANDLE_VALUE(); |
|
182
|
0
|
0
|
|
|
|
0
|
if ( SetHandleInformation( $os_fhandle, HANDLE_FLAG_INHERIT(), 0) ) { |
|
183
|
0
|
|
|
|
|
0
|
_debug( "# set no-inherit flag on $which tee\n" ); |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
else { |
|
186
|
0
|
|
|
|
|
0
|
_debug( "# can't disable tee handle flag inherit: " . fileLastError() . "\n"); |
|
187
|
|
|
|
|
|
|
} |
|
188
|
0
|
|
|
|
|
0
|
_open_std( $stash->{child}{$which} ); |
|
189
|
0
|
|
|
|
|
0
|
$stash->{pid}{$which} = system(1, @cmd, $stash->{flag_files}{$which}); |
|
190
|
|
|
|
|
|
|
# not restoring std here as it all gets redirected again shortly anyway |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
else { # use fork |
|
193
|
41855
|
|
|
|
|
215054
|
_fork_exec( $which, $stash ); |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub _fork_exec { |
|
198
|
41855
|
|
|
41855
|
|
78378
|
my ($which, $stash) = @_; |
|
199
|
41855
|
|
|
|
|
136928610
|
my $pid = fork; |
|
200
|
41855
|
50
|
|
|
|
1681537
|
if ( not defined $pid ) { |
|
|
|
100
|
|
|
|
|
|
|
201
|
0
|
|
|
|
|
0
|
Carp::confess "Couldn't fork(): $!"; |
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
elsif ($pid == 0) { # child |
|
204
|
902
|
|
|
|
|
153237
|
_debug( "# in child process ...\n" ); |
|
205
|
902
|
|
|
|
|
44072
|
untie *STDIN; untie *STDOUT; untie *STDERR; |
|
|
902
|
|
|
|
|
28588
|
|
|
|
902
|
|
|
|
|
16603
|
|
|
206
|
902
|
|
|
|
|
86266
|
_close $stash->{tee}{$which}; |
|
207
|
902
|
|
|
|
|
26957
|
_debug( "# redirecting handles in child ...\n" ); |
|
208
|
902
|
|
|
|
|
34140
|
_open_std( $stash->{child}{$which} ); |
|
209
|
902
|
|
|
|
|
19470
|
_debug( "# calling exec on command ...\n" ); |
|
210
|
902
|
|
|
|
|
0
|
exec @cmd, $stash->{flag_files}{$which}; |
|
211
|
|
|
|
|
|
|
} |
|
212
|
40953
|
|
|
|
|
6172042
|
$stash->{pid}{$which} = $pid |
|
213
|
|
|
|
|
|
|
} |
|
214
|
|
|
|
|
|
|
|
|
215
|
1648132196
|
|
100
|
1648132196
|
|
29818225143
|
sub _files_exist { -f $_ || return 0 for @_; return 1 } |
|
|
42302
|
|
|
|
|
320187
|
|
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub _wait_for_tees { |
|
218
|
21151
|
|
|
21151
|
|
101462
|
my ($stash) = @_; |
|
219
|
21151
|
|
|
|
|
82694
|
my $start = time; |
|
220
|
21151
|
|
|
|
|
45490
|
my @files = values %{$stash->{flag_files}}; |
|
|
21151
|
|
|
|
|
1595870
|
|
|
221
|
21151
|
50
|
|
|
|
412806
|
my $timeout = defined $ENV{PERL_CAPTURE_TINY_TIMEOUT} |
|
222
|
|
|
|
|
|
|
? $ENV{PERL_CAPTURE_TINY_TIMEOUT} : $TIMEOUT; |
|
223
|
21151
|
|
33
|
|
|
315123
|
1 until _files_exist(@files) || ($timeout && (time - $start > $timeout)); |
|
|
|
|
66
|
|
|
|
|
|
224
|
21151
|
50
|
|
|
|
150952
|
Carp::confess "Timed out waiting for subprocesses to start" if ! _files_exist(@files); |
|
225
|
21151
|
|
|
|
|
25668269
|
unlink $_ for @files; |
|
226
|
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub _kill_tees { |
|
229
|
21151
|
|
|
21151
|
|
58567
|
my ($stash) = @_; |
|
230
|
21151
|
50
|
|
|
|
243521
|
if ( $IS_WIN32 ) { |
|
231
|
0
|
|
|
|
|
0
|
_debug( "# closing handles with CloseHandle\n"); |
|
232
|
0
|
|
|
|
|
0
|
CloseHandle( GetOsFHandle($_) ) for values %{ $stash->{tee} }; |
|
|
0
|
|
|
|
|
0
|
|
|
233
|
0
|
|
|
|
|
0
|
_debug( "# waiting for subprocesses to finish\n"); |
|
234
|
0
|
|
|
|
|
0
|
my $start = time; |
|
235
|
0
|
|
0
|
|
|
0
|
1 until wait == -1 || (time - $start > 30); |
|
236
|
|
|
|
|
|
|
} |
|
237
|
|
|
|
|
|
|
else { |
|
238
|
21151
|
|
|
|
|
48089
|
_close $_ for values %{ $stash->{tee} }; |
|
|
21151
|
|
|
|
|
250854
|
|
|
239
|
21151
|
|
|
|
|
73191
|
waitpid $_, 0 for values %{ $stash->{pid} }; |
|
|
21151
|
|
|
|
|
34828656
|
|
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub _slurp { |
|
244
|
156702
|
|
|
156702
|
|
1691090
|
seek $_[0],0,0; local $/; return scalar readline $_[0]; |
|
|
156702
|
|
|
|
|
976022
|
|
|
|
156702
|
|
|
|
|
7359160
|
|
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
248
|
|
|
|
|
|
|
# _capture_tee() -- generic main sub for capturing or teeing |
|
249
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub _capture_tee { |
|
252
|
88390
|
|
|
88390
|
|
1047125
|
_debug( "# starting _capture_tee with (@_)...\n" ); |
|
253
|
88390
|
|
|
|
|
571489
|
my ($tee_stdout, $tee_stderr, $merge, $code, $files) = @_; |
|
254
|
|
|
|
|
|
|
# save existing filehandles and setup captures |
|
255
|
88390
|
|
|
|
|
582347
|
local *CT_ORIG_STDIN = *STDIN ; |
|
256
|
88390
|
|
|
|
|
456505
|
local *CT_ORIG_STDOUT = *STDOUT; |
|
257
|
88390
|
|
|
|
|
375987
|
local *CT_ORIG_STDERR = *STDERR; |
|
258
|
|
|
|
|
|
|
# find initial layers |
|
259
|
88390
|
|
|
|
|
1793124
|
my %layers = ( |
|
260
|
|
|
|
|
|
|
stdin => [PerlIO::get_layers(\*STDIN) ], |
|
261
|
|
|
|
|
|
|
stdout => [PerlIO::get_layers(\*STDOUT)], |
|
262
|
|
|
|
|
|
|
stderr => [PerlIO::get_layers(\*STDERR)], |
|
263
|
|
|
|
|
|
|
); |
|
264
|
88390
|
|
|
|
|
639592
|
_debug( "# existing layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; |
|
|
265170
|
|
|
|
|
1450927
|
|
|
265
|
|
|
|
|
|
|
# bypass scalar filehandles and tied handles |
|
266
|
88390
|
|
|
|
|
195212
|
my %localize; |
|
267
|
88390
|
100
|
|
|
|
154967
|
$localize{stdin}++, local(*STDIN) if grep { $_ eq 'scalar' } @{$layers{stdin}}; |
|
|
155423
|
|
|
|
|
675006
|
|
|
|
88390
|
|
|
|
|
282129
|
|
|
268
|
88390
|
100
|
|
|
|
180228
|
$localize{stdout}++, local(*STDOUT) if grep { $_ eq 'scalar' } @{$layers{stdout}}; |
|
|
182312
|
|
|
|
|
613983
|
|
|
|
88390
|
|
|
|
|
250121
|
|
|
269
|
88390
|
100
|
|
|
|
156975
|
$localize{stderr}++, local(*STDERR) if grep { $_ eq 'scalar' } @{$layers{stderr}}; |
|
|
182282
|
|
|
|
|
521827
|
|
|
|
88390
|
|
|
|
|
216891
|
|
|
270
|
88390
|
100
|
66
|
|
|
540341
|
$localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") if tied *STDOUT && $] >= 5.008; |
|
271
|
88390
|
100
|
66
|
|
|
423495
|
$localize{stderr}++, local(*STDERR), _open( \*STDERR, ">&=2") if tied *STDERR && $] >= 5.008; |
|
272
|
88390
|
|
|
|
|
472944
|
_debug( "# localized $_\n" ) for keys %localize; |
|
273
|
88390
|
|
|
|
|
327846
|
my %proxy_std = _proxy_std(); |
|
274
|
88390
|
|
|
|
|
169086
|
_debug( "# proxy std is @{ [%proxy_std] }\n" ); |
|
|
88390
|
|
|
|
|
537745
|
|
|
275
|
88390
|
|
|
|
|
386259
|
my $stash = { old => _copy_std() }; |
|
276
|
|
|
|
|
|
|
# update layers after any proxying |
|
277
|
88390
|
|
|
|
|
2249898
|
%layers = ( |
|
278
|
|
|
|
|
|
|
stdin => [PerlIO::get_layers(\*STDIN) ], |
|
279
|
|
|
|
|
|
|
stdout => [PerlIO::get_layers(\*STDOUT)], |
|
280
|
|
|
|
|
|
|
stderr => [PerlIO::get_layers(\*STDERR)], |
|
281
|
|
|
|
|
|
|
); |
|
282
|
88390
|
|
|
|
|
550110
|
_debug( "# post-proxy layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/; |
|
|
265170
|
|
|
|
|
1377859
|
|
|
283
|
|
|
|
|
|
|
# get handles for capture and apply existing IO layers |
|
284
|
88390
|
|
|
|
|
413830
|
$stash->{new}{$_} = $stash->{capture}{$_} = _capture_file( $_, $files ) for qw/stdout stderr/; |
|
285
|
88390
|
|
|
|
|
46599705
|
_debug("# will capture $_ on " .fileno($stash->{capture}{$_})."\n" ) for qw/stdout stderr/; |
|
286
|
|
|
|
|
|
|
# tees may change $stash->{new} |
|
287
|
88390
|
100
|
|
|
|
400212
|
_start_tee( stdout => $stash ) if $tee_stdout; |
|
288
|
87849
|
100
|
|
|
|
2406876
|
_start_tee( stderr => $stash ) if $tee_stderr; |
|
289
|
87488
|
100
|
66
|
|
|
2939822
|
_wait_for_tees( $stash ) if $tee_stdout || $tee_stderr; |
|
290
|
|
|
|
|
|
|
# finalize redirection |
|
291
|
87488
|
100
|
|
|
|
369504
|
$stash->{new}{stderr} = $stash->{new}{stdout} if $merge; |
|
292
|
87488
|
|
|
|
|
1874161
|
$stash->{new}{stdin} = $stash->{old}{stdin}; |
|
293
|
87488
|
|
|
|
|
253794
|
_debug( "# redirecting in parent ...\n" ); |
|
294
|
87488
|
|
|
|
|
576987
|
_open_std( $stash->{new} ); |
|
295
|
|
|
|
|
|
|
# execute user provided code |
|
296
|
87488
|
|
|
|
|
220690
|
my ($exit_code, $inner_error, $outer_error, @user_code_result); |
|
297
|
|
|
|
|
|
|
{ |
|
298
|
87488
|
100
|
|
|
|
148514
|
local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN |
|
|
87488
|
|
|
|
|
330062
|
|
|
299
|
87488
|
100
|
|
|
|
293486
|
local *STDERR = *STDOUT if $merge; # minimize buffer mixups during $code |
|
300
|
87488
|
|
|
|
|
274684
|
_debug( "# finalizing layers ...\n" ); |
|
301
|
87488
|
|
|
|
|
521888
|
_relayer(\*STDOUT, $layers{stdout}); |
|
302
|
87488
|
100
|
|
|
|
404558
|
_relayer(\*STDERR, $layers{stderr}) unless $merge; |
|
303
|
87488
|
|
|
|
|
382229
|
_debug( "# running code $code ...\n" ); |
|
304
|
87488
|
|
|
|
|
144810
|
local $@; |
|
305
|
87488
|
|
|
|
|
196616
|
@user_code_result = eval { |
|
306
|
87488
|
|
|
|
|
1177552
|
my @res = $code->(); |
|
307
|
86585
|
|
|
|
|
444926926
|
$inner_error = $@; |
|
308
|
86585
|
|
|
|
|
771029
|
return @res; |
|
309
|
|
|
|
|
|
|
}; |
|
310
|
86586
|
|
|
|
|
782203
|
$exit_code = $?; # save this for later |
|
311
|
86586
|
|
|
|
|
643881
|
$outer_error = $@; # save this for later |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
# restore prior filehandles and shut down tees |
|
314
|
86586
|
|
|
|
|
376053
|
_debug( "# restoring ...\n" ); |
|
315
|
86586
|
|
|
|
|
971744
|
_open_std( $stash->{old} ); |
|
316
|
86586
|
|
|
|
|
223371
|
_close( $_ ) for values %{$stash->{old}}; # don't leak fds |
|
|
86586
|
|
|
|
|
969864
|
|
|
317
|
86586
|
|
|
|
|
914612
|
_unproxy( %proxy_std ); |
|
318
|
86586
|
100
|
66
|
|
|
1763131
|
_kill_tees( $stash ) if $tee_stdout || $tee_stderr; |
|
319
|
|
|
|
|
|
|
# return captured output |
|
320
|
86586
|
|
|
|
|
812720
|
_relayer($stash->{capture}{stdout}, $layers{stdout}); |
|
321
|
86586
|
100
|
|
|
|
625982
|
_relayer($stash->{capture}{stderr}, $layers{stderr}) unless $merge; |
|
322
|
86586
|
|
|
|
|
556245
|
_debug( "# slurping captured $_ with layers: @{[PerlIO::get_layers($stash->{capture}{$_})]}\n") for qw/stdout stderr/; |
|
|
173172
|
|
|
|
|
2408166
|
|
|
323
|
86586
|
|
|
|
|
580195
|
my $got_out = _slurp($stash->{capture}{stdout}); |
|
324
|
86586
|
100
|
|
|
|
486405
|
my $got_err = $merge ? q() : _slurp($stash->{capture}{stderr}); |
|
325
|
86586
|
50
|
66
|
|
|
517625
|
print CT_ORIG_STDOUT $got_out if $localize{stdout} && $tee_stdout; |
|
326
|
86586
|
50
|
100
|
|
|
897895
|
print CT_ORIG_STDERR $got_err if !$merge && $localize{stderr} && $tee_stdout; |
|
|
|
|
66
|
|
|
|
|
|
327
|
86586
|
|
|
|
|
249956
|
$? = $exit_code; |
|
328
|
86586
|
100
|
|
|
|
253831
|
$@ = $inner_error if $inner_error; |
|
329
|
86586
|
100
|
|
|
|
254733
|
die $outer_error if $outer_error; |
|
330
|
86585
|
|
|
|
|
730201
|
_debug( "# ending _capture_tee with (@_)...\n" ); |
|
331
|
86585
|
50
|
|
|
|
818941
|
return wantarray ? ($got_out, @user_code_result) : $got_out if $merge; |
|
|
|
100
|
|
|
|
|
|
|
332
|
70115
|
100
|
|
|
|
2790542
|
return wantarray ? ($got_out, $got_err, @user_code_result) : $got_out; |
|
333
|
|
|
|
|
|
|
} |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
336
|
|
|
|
|
|
|
# capture to files |
|
337
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
sub _capture_file { |
|
340
|
176780
|
|
|
176780
|
|
74521194
|
my ( $target, $files ) = @_; |
|
341
|
|
|
|
|
|
|
|
|
342
|
176780
|
50
|
|
|
|
2676877
|
return File::Temp->new if !$files->{$target}; |
|
343
|
|
|
|
|
|
|
|
|
344
|
0
|
0
|
0
|
|
|
0
|
Carp::confess "$target file '$files->{$target}' already exists, set clobber => 1 to override" |
|
345
|
|
|
|
|
|
|
if $files->{new_files} and _files_exist( $files->{$target} ); |
|
346
|
|
|
|
|
|
|
|
|
347
|
0
|
|
|
|
|
0
|
my $mode = "+>>"; |
|
348
|
0
|
0
|
|
|
|
0
|
$mode = "+>" if $files->{clobber}; |
|
349
|
|
|
|
|
|
|
|
|
350
|
0
|
|
|
|
|
0
|
my $fh = Symbol::gensym; |
|
351
|
0
|
|
|
|
|
0
|
_open $fh, "$mode$files->{$target}"; |
|
352
|
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
0
|
return $fh; |
|
354
|
|
|
|
|
|
|
} |
|
355
|
|
|
|
|
|
|
|
|
356
|
0
|
|
|
0
|
1
|
0
|
sub capture_files { return { @_ }; } |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
359
|
|
|
|
|
|
|
# create API subroutines from [tee STDOUT flag, tee STDERR, merge flag] |
|
360
|
|
|
|
|
|
|
#--------------------------------------------------------------------------# |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
my %api = ( |
|
363
|
|
|
|
|
|
|
capture => [0,0,0], |
|
364
|
|
|
|
|
|
|
capture_merged => [0,0,1], |
|
365
|
|
|
|
|
|
|
tee => [1,1,0], |
|
366
|
|
|
|
|
|
|
tee_merged => [1,0,1], # don't tee STDOUT since merging |
|
367
|
|
|
|
|
|
|
); |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
for my $sub ( keys %api ) { |
|
370
|
|
|
|
|
|
|
my $args = join q{, }, @{$api{$sub}}; |
|
371
|
51577
|
|
|
51577
|
0
|
272783181
|
eval "sub $sub(&;\$) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic |
|
|
51577
|
|
|
14760
|
0
|
738187
|
|
|
|
14760
|
|
|
20163
|
0
|
64452923
|
|
|
|
14760
|
|
|
1890
|
0
|
115818
|
|
|
|
20163
|
|
|
|
|
1787887
|
|
|
|
20163
|
|
|
|
|
177136
|
|
|
|
1890
|
|
|
|
|
99044
|
|
|
|
1890
|
|
|
|
|
17817
|
|
|
372
|
|
|
|
|
|
|
} |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
1; |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=pod |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=head1 NAME |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
Capture::Tiny::Extended - Capture STDOUT and STDERR from from Perl, XS or external programs (with some extras) |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=head1 VERSION |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
version 0.114 |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
use Capture::Tiny::Extended qw/capture tee capture_merged tee_merged/; |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
# capture return values |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
my ($stdout, $stderr, @return) = capture { |
|
395
|
|
|
|
|
|
|
# your code here |
|
396
|
|
|
|
|
|
|
return system( 'ls' ); |
|
397
|
|
|
|
|
|
|
}; |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
($merged, @return) = capture_merged { |
|
400
|
|
|
|
|
|
|
# your code here |
|
401
|
|
|
|
|
|
|
return system( 'ls' ); |
|
402
|
|
|
|
|
|
|
}; |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# or use explicit capture files |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
($stdout, $stderr, @return) = capture( |
|
407
|
|
|
|
|
|
|
sub { # your code here }, |
|
408
|
|
|
|
|
|
|
{ stdout => 'stdout.log' } |
|
409
|
|
|
|
|
|
|
); |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# or with sugar |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
use Capture::Tiny::Extended qw/capture tee capture_merged tee_merged capture_files/; |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
($stdout, $stderr, @return) = capture { |
|
416
|
|
|
|
|
|
|
# your code here |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
capture_files ( |
|
419
|
|
|
|
|
|
|
stdout => 'stdout.log', |
|
420
|
|
|
|
|
|
|
stderr => 'stderr.log', |
|
421
|
|
|
|
|
|
|
); |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
Capture::Tiny::Extended is a fork of L. It is functionally |
|
426
|
|
|
|
|
|
|
identical with the parent module, except for the differences documented in this |
|
427
|
|
|
|
|
|
|
POD. Please see the documentation of L for details on standard |
|
428
|
|
|
|
|
|
|
usage. |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
Please note that this can be considered an experimental module in some respects. |
|
431
|
|
|
|
|
|
|
I am not as experienced with the subject matter (and in general) as David Golden |
|
432
|
|
|
|
|
|
|
and mostly implemented these features here because i needed them fast and did |
|
433
|
|
|
|
|
|
|
not have the time to spare to wait for them to get into L. If you |
|
434
|
|
|
|
|
|
|
need capture functionality for mission-critical parts, consider whether |
|
435
|
|
|
|
|
|
|
L might be enough for the job. |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
Of course I will however make all efforts to make this as stable and useful as |
|
438
|
|
|
|
|
|
|
possible by keeping it up-to-date (as my time permits) with changes and bugfixes |
|
439
|
|
|
|
|
|
|
applied to L, as well as responding and addressing and change |
|
440
|
|
|
|
|
|
|
requests or bug reports for this module. |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=for Pod::Coverage capture capture_merged tee tee_merged |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=head1 DIFFERENCES |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head2 Capturing Return Values |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
When executing code within a capture you sometimes want to also keep the return |
|
449
|
|
|
|
|
|
|
value, for example when capturing a system() call. In Capture::Tiny this has to |
|
450
|
|
|
|
|
|
|
be done like this: |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
use Capture::Tiny 'capture'; |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
my $res; |
|
455
|
|
|
|
|
|
|
my ( $out, $err ) = capture { |
|
456
|
|
|
|
|
|
|
$res = system( 'ls' ); |
|
457
|
|
|
|
|
|
|
}; |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
Capture::Tiny::Extended automatically captures return values and returns them |
|
460
|
|
|
|
|
|
|
after the second return value (or first if you're using the merged functions). |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
use Capture::Tiny::Extended 'capture'; |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
my ( $out, $err, $res ) = capture { system( 'ls' ) }; |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=head2 Teeing In Realtime |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
Sometimes you want to use Capture::Tiny to capture any and all output of an |
|
469
|
|
|
|
|
|
|
action and dump it into a log file, while also displaying it on the screen and |
|
470
|
|
|
|
|
|
|
then post-process the results later on (for example for sending status mails). |
|
471
|
|
|
|
|
|
|
The only way to do this with Capture::Tiny is code like this: |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
use Capture::Tiny 'capture'; |
|
474
|
|
|
|
|
|
|
use File::Slurp; |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
my $res; |
|
477
|
|
|
|
|
|
|
my ( $out, $err ) = capture { |
|
478
|
|
|
|
|
|
|
# lockfile and other processing here along with debug output |
|
479
|
|
|
|
|
|
|
$res = system( 'long_running_program' ); |
|
480
|
|
|
|
|
|
|
}; |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
file_write 'out.log', $out; |
|
483
|
|
|
|
|
|
|
send_mail( $err ) if $res; |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
This has a very big disadvantage. If the long-running program runs too long, and |
|
486
|
|
|
|
|
|
|
the perl script is started by something like crontab there is no way for you to |
|
487
|
|
|
|
|
|
|
get at the log output. You will have to wait for it to complete before the |
|
488
|
|
|
|
|
|
|
captured output is written to the file. |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
Capture::Tiny::Extended gives you the option to provide filenames for it to use |
|
491
|
|
|
|
|
|
|
as capture buffers. This means the output from the captured code will appear on |
|
492
|
|
|
|
|
|
|
the screen and in the file in realtime, and will afterwards be available to your |
|
493
|
|
|
|
|
|
|
Perl script in the variables returned by the capture function: |
|
494
|
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
use Capture::Tiny::Extended 'capture'; |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
my ( $out, $err, $res ) = capture( |
|
498
|
|
|
|
|
|
|
sub { |
|
499
|
|
|
|
|
|
|
# lockfile and other processing here along with debug output |
|
500
|
|
|
|
|
|
|
return system( 'long_running_program' ); |
|
501
|
|
|
|
|
|
|
}, |
|
502
|
|
|
|
|
|
|
{ |
|
503
|
|
|
|
|
|
|
stdout => 'out.log', |
|
504
|
|
|
|
|
|
|
stderr => 'err.log', |
|
505
|
|
|
|
|
|
|
} |
|
506
|
|
|
|
|
|
|
); |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
send_mail( $err ) if $res; |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=head2 capture_files |
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
Since using hashes in that way breaks a bit of the syntax magic of the capture |
|
513
|
|
|
|
|
|
|
functions (or makes them harder to read), there exists a sugar function to take |
|
514
|
|
|
|
|
|
|
the file arguments and pass it on to the capture functions: |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
use Capture::Tiny::Extended qw( capture capture_files ); |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
my ( $out, $err, $res ) = capture { |
|
519
|
|
|
|
|
|
|
# lockfile and other processing here along with debug output |
|
520
|
|
|
|
|
|
|
return system( 'long_running_program' ); |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
capture_files { |
|
523
|
|
|
|
|
|
|
stdout => 'out.log', |
|
524
|
|
|
|
|
|
|
stderr => 'err.log', |
|
525
|
|
|
|
|
|
|
}; |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
send_mail( $err ) if $res; |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=head2 Capture File Mode Options |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
For purposes of avoiding data loss, the default behavior is to append to the |
|
532
|
|
|
|
|
|
|
specified files. The key 'new_files' can be set to a true value on the extra |
|
533
|
|
|
|
|
|
|
file hash parameter to instruct Capture::Tiny::Extended to attempt to make |
|
534
|
|
|
|
|
|
|
files. It will die however if the specified files already exist. |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
use Capture::Tiny::Extended 'capture'; |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
my $out = capture_merged( |
|
539
|
|
|
|
|
|
|
sub { system( 'ls' ) }, |
|
540
|
|
|
|
|
|
|
{ stdout => 'out.log', new_files => 1 } |
|
541
|
|
|
|
|
|
|
); |
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
If existing files should always be overwritten, no matter what, the key |
|
544
|
|
|
|
|
|
|
'clobber' can be set instead: |
|
545
|
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
use Capture::Tiny::Extended 'capture'; |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
my $out = capture_merged( |
|
549
|
|
|
|
|
|
|
sub { system( 'ls' ) }, |
|
550
|
|
|
|
|
|
|
{ stdout => 'out.log', clobber => 1 } |
|
551
|
|
|
|
|
|
|
); |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=head1 WHY A FORK? |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
The realtime teeing feature was very important for one of my current projects |
|
556
|
|
|
|
|
|
|
and i needed it on CPAN to be able to easily distribute it to many systems. |
|
557
|
|
|
|
|
|
|
I had provided a patch for the return value capturing on Github to David Golden |
|
558
|
|
|
|
|
|
|
a long while ago, but due to being busy with real life, family and more |
|
559
|
|
|
|
|
|
|
important projects than this he was not able to find time to proof and integrate |
|
560
|
|
|
|
|
|
|
it and in the foreseeable future won't be able to either. At the same time i |
|
561
|
|
|
|
|
|
|
lack the Perl file handle, descriptor and layer chops to take full |
|
562
|
|
|
|
|
|
|
responsibility for Capture::Tiny itself. Usually i would have just written a |
|
563
|
|
|
|
|
|
|
subclass of the original, but since Capture::Tiny is written in functional style |
|
564
|
|
|
|
|
|
|
this was not possible. |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
As such a fork seemed to be the best option to get these features out there. I'd |
|
567
|
|
|
|
|
|
|
be more than happy to see them integrated into C::T someday and will keep my git |
|
568
|
|
|
|
|
|
|
repository in such a state as to make this as easy as possible. (Lots of |
|
569
|
|
|
|
|
|
|
rebasing.) |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
|
572
|
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
Capture::Tiny is an invaluable tool that uses practically indecent amounts of |
|
574
|
|
|
|
|
|
|
creativity to solve decidedly nontrivial problems and circumvents many cliffs |
|
575
|
|
|
|
|
|
|
the ordinary coder (and most certainly me) would inevitably crash against. |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
Many thanks to David Golden for taking the time and braving all those traps of |
|
578
|
|
|
|
|
|
|
insanity to create Capture::Tiny. |
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=head1 SUPPORT |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=head2 Bugs / Feature Requests |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
Please report any bugs or feature requests by email to C, or through |
|
587
|
|
|
|
|
|
|
the web interface at L. You will be automatically notified of any |
|
588
|
|
|
|
|
|
|
progress on the request by the system. |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
=head2 Source Code |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
This is open source software. The code repository is available for |
|
593
|
|
|
|
|
|
|
public review and contribution under the terms of the license. |
|
594
|
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
L |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
git clone https://github.com/wchristian/capture-tiny |
|
598
|
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=head1 AUTHORS |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=over 4 |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
=item * |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
Christian Walde |
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
=item * |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
David Golden |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=back |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
This software is Copyright (c) 2009 by David Golden. |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
This is free software, licensed under: |
|
618
|
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
The Apache License, Version 2.0, January 2004 |
|
620
|
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=cut |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
__END__ |