File Coverage

blib/lib/IPC/Open3/Callback/Command.pm
Criterion Covered Total %
statement 15 139 10.7
branch 0 74 0.0
condition 0 28 0.0
subroutine 5 23 21.7
pod n/a
total 20 264 7.5


line stmt bran cond sub pod time code
1 1     1   15932 use strict;
  1         2  
  1         35  
2 1     1   4 use warnings;
  1         1  
  1         55  
3              
4             package IPC::Open3::Callback::Command;
5             $IPC::Open3::Callback::Command::VERSION = '1.16';
6             # ABSTRACT: A utility class that provides subroutines for building shell command strings.
7              
8 1     1   3 use Exporter qw(import);
  1         1  
  1         46  
9             our @EXPORT_OK =
10             qw(batch_command command command_options cp_command mkdir_command pipe_command rm_command sed_command write_command);
11              
12 1     1   4 use File::Spec;
  1         1  
  1         1472  
13              
14             sub batch_command {
15 0     0     my ( @commands, $batch_options, $command_options );
16 0           (@commands) = @_;
17 0 0         $command_options = pop(@commands)
18             if ( ref( $commands[$#commands] ) eq 'IPC::Open3::Callback::Command::CommandOptions' );
19 0 0         $batch_options = pop(@commands) if ( ref( $commands[$#commands] ) eq 'HASH' );
20              
21 0 0         push( @commands, $command_options ) if ($command_options);
22              
23             wrap(
24             $batch_options || {},
25             @commands,
26             sub {
27 0     0     return @_;
28             }
29 0   0       );
30             }
31              
32             sub command_options {
33 0     0     return IPC::Open3::Callback::Command::CommandOptions->new(@_);
34             }
35              
36             sub command {
37             wrap(
38             {},
39             @_,
40             sub {
41 0     0     return shift;
42             }
43 0     0     );
44             }
45              
46             sub cp_command {
47 0     0     my ( $source_path, $source_command_options, $destination_path, $destination_command_options,
48             %cp_options );
49 0           $source_path = shift;
50 0 0         $source_command_options = shift
51             if ( ref( $_[0] ) eq 'IPC::Open3::Callback::Command::CommandOptions' );
52 0           $destination_path = shift;
53 0 0         $destination_command_options = shift
54             if ( ref( $_[0] ) eq 'IPC::Open3::Callback::Command::CommandOptions' );
55 0           %cp_options = @_;
56              
57 0 0         $source_command_options = command_options() if ( !$source_command_options );
58 0 0         $destination_command_options = command_options() if ( !$destination_command_options );
59              
60 0           my $source_command;
61             my $destination_command;
62 0 0         if ( $cp_options{file} ) {
63              
64             # is a file, so use cat | dd
65 0 0         if ( $cp_options{compress} ) {
66 0           $source_command = command( "gzip -c $source_path", $source_command_options );
67 0           $destination_command =
68             pipe_command( "gunzip", "dd of=$destination_path", $destination_command_options );
69             }
70             else {
71 0           $source_command = command( "cat $source_path", $source_command_options );
72 0           $destination_command =
73             command( "dd of=$destination_path", $destination_command_options );
74             }
75             }
76             else {
77             # is a directory, so use tar or unzip
78 0 0 0       if ( $cp_options{archive} && $cp_options{archive} eq 'zip' ) {
79 0   0       my $temp_zip = File::Spec->catfile( $destination_path,
80             $cp_options{unzip_temp_file} || "temp_cp_command.zip" );
81 0           $source_command =
82             command(
83             batch_command( "cd $source_path", "zip -qr - .", { subshell => "bash -c " } ),
84             $source_command_options );
85 0           $destination_command = batch_command(
86             "dd of=$temp_zip", "unzip -qod $destination_path $temp_zip",
87             rm_command($temp_zip), $destination_command_options
88             );
89             }
90             else {
91             # default, use tar
92 0           my @parts = ("tar c -C $source_path .");
93 0           my @destination_parts = ();
94 0 0         if ( $cp_options{status} ) {
95 0   0       push(
96             @parts,
97             join(
98             '',
99             'pv -f -s `',
100             _sudo_command(
101             $source_command_options
102             && $source_command_options->get_sudo_username(),
103             pipe_command( "du -sb $source_path", 'cut -f1' )
104             ),
105             '`'
106             )
107             );
108             }
109 0 0         if ( $cp_options{compress} ) {
110 0           push( @parts, 'gzip' );
111 0           push( @destination_parts, 'gunzip' );
112             }
113             push(
114 0   0       @destination_parts,
115             _sudo_command(
116             $destination_command_options
117             && $destination_command_options->get_sudo_username(),
118             "tar x -C $destination_path"
119             )
120             );
121              
122 0           $source_command = command( pipe_command(@parts), $source_command_options );
123 0           $destination_command = command( pipe_command(@destination_parts),
124             $destination_command_options->clone( sudo_username => undef ) );
125             }
126             }
127              
128 0           return pipe_command( $source_command, $destination_command );
129             }
130              
131             sub mkdir_command {
132             wrap(
133             {},
134             @_,
135             sub {
136 0     0     return 'mkdir -p "' . join( '" "', @_ ) . '"';
137             }
138 0     0     );
139             }
140              
141             sub pipe_command {
142             wrap(
143             { command_separator => '|' },
144             @_,
145             sub {
146 0     0     return @_;
147             }
148 0     0     );
149             }
150              
151             sub _quote_command {
152 0     0     my ($command) = @_;
153 0           $command =~ s/\\/\\\\/g;
154 0           $command =~ s/`/\\`/g; # for `command`
155 0           $command =~ s/"/\\"/g;
156 0           return "\"$command\"";
157             }
158              
159             sub rm_command {
160             wrap(
161             {},
162             @_,
163             sub {
164 0     0     return 'rm -rf "' . join( '" "', @_ ) . '"';
165             }
166 0     0     );
167             }
168              
169             sub sed_command {
170             wrap(
171             {},
172             @_,
173             sub {
174 0     0     my @args = @_;
175 0           my $options = {};
176              
177 0 0         if ( ref( $args[$#args] ) eq 'HASH' ) {
178 0           $options = pop(@args);
179             }
180              
181 0           my $command = 'sed';
182 0 0         $command .= ' -i' if ( $options->{in_place} );
183 0 0         if ( defined( $options->{temp_script_file} ) ) {
184 0           my $temp_script_file_name = $options->{temp_script_file}->filename();
185 0 0         print( { $options->{temp_script_file} } join( ' ', '', map {"$_;"} @args ) )
  0            
  0            
186             if ( scalar(@args) );
187 0           print(
188 0           { $options->{temp_script_file} } join( ' ',
189             '',
190 0           map {"s/$_/$options->{replace_map}{$_}/g;"}
191 0 0         keys( %{ $options->{replace_map} } ) )
192             ) if ( defined( $options->{replace_map} ) );
193 0           $options->{temp_script_file}->flush();
194 0           $command .= " -f $temp_script_file_name";
195             }
196             else {
197 0 0         $command .= join( ' ', '', map {"-e '$_'"} @args ) if ( scalar(@args) );
  0            
198 0           $command .= join( ' ',
199             '',
200 0           map {"-e 's/$_/$options->{replace_map}{$_}/g'"}
201 0 0         keys( %{ $options->{replace_map} } ) )
202             if ( defined( $options->{replace_map} ) );
203             }
204 0 0         $command .= join( ' ', '', @{ $options->{files} } ) if ( $options->{files} );
  0            
205              
206 0           return $command;
207             }
208 0     0     );
209             }
210              
211             sub _sudo_command {
212 0     0     my ( $sudo_username, $command ) = @_;
213 0 0         if ( defined($sudo_username) ) {
214 0 0         $command = "sudo " . ( $sudo_username ? "-u $sudo_username " : '' ) . $command;
215             }
216 0           return $command;
217             }
218              
219             sub write_command {
220 0     0     my ( $filename, @lines, $write_options, $command_options );
221 0           $filename = shift;
222 0           @lines = @_;
223 0 0         $command_options = pop(@lines)
224             if ( ref( $lines[$#lines] ) eq 'IPC::Open3::Callback::Command::CommandOptions' );
225 0 0         $write_options = pop(@lines) if ( ref( $lines[$#lines] ) eq 'HASH' );
226              
227 0           my $remote_command = "dd of=$filename";
228 0 0 0       if ( defined($write_options) && defined( $write_options->{mode} ) ) {
    0          
229 0 0         if ( defined($command_options) ) {
    0          
230 0           $remote_command =
231             batch_command( $remote_command, "chmod $write_options->{mode} $filename",
232             $command_options );
233             }
234             elsif ( defined($command_options) ) {
235 0           $remote_command =
236             batch_command( $remote_command, "chmod $write_options->{mode} $filename" );
237             }
238             }
239             elsif ( defined($command_options) ) {
240 0           $remote_command = command( $remote_command, $command_options );
241             }
242              
243 0 0 0       my $line_separator =
244             ( defined($write_options) && defined( $write_options->{line_separator} ) )
245             ? $write_options->{line_separator}
246             : '\n';
247 0           return pipe_command( 'printf "' . join( $line_separator, @lines ) . '"', $remote_command );
248             }
249              
250             # Handles wrapping commands with possible ssh and command prefix
251             sub wrap {
252 0     0     my $wrap_options = shift;
253 0           my $builder = pop;
254 0           my @args = @_;
255 0           my ( $ssh, $username, $hostname, $sudo_username, $pretty );
256              
257 0 0         if ( ref( $args[$#args] ) eq 'IPC::Open3::Callback::Command::CommandOptions' ) {
258 0           my $options = pop(@args);
259 0   0       $ssh = $options->get_ssh() || 'ssh';
260 0           $username = $options->get_username();
261 0           $hostname = $options->get_hostname();
262 0           $sudo_username = $options->get_sudo_username();
263 0           $pretty = $options->get_pretty();
264             }
265              
266 0           my $destination_command = '';
267 0   0       my $command_separator = $wrap_options->{command_separator} || ';';
268 0           my $commands = 0;
269 0           foreach my $command ( &$builder(@args) ) {
270 0 0         if ( defined($command) ) {
271 0 0         if ( $commands++ > 0 ) {
272 0           $destination_command .= $command_separator;
273 0 0         if ($pretty) {
274 0           $destination_command .= "\n";
275             }
276             }
277              
278 0           $command =~ s/^(.*?[^\\]);$/$1/; # from find -exec
279              
280 0           $command = _sudo_command( $sudo_username, $command );
281              
282 0           $destination_command .= $command;
283             }
284             }
285              
286 0 0         if ( $wrap_options->{subshell} ) {
287 0           $destination_command = $wrap_options->{subshell} . _quote_command($destination_command);
288             }
289              
290 0 0 0       if ( !defined($username) && !defined($hostname) ) {
291              
292             # silly to ssh to localhost as current user, so dont
293 0           return $destination_command;
294             }
295              
296 0 0         my $userAt =
    0          
297             $username
298             ? ( ( $ssh =~ /plink(?:\.exe)?$/ ) ? "-l $username " : "$username\@" )
299             : '';
300              
301 0           $destination_command = _quote_command($destination_command);
302 0   0       return "$ssh $userAt" . ( $hostname || 'localhost' ) . " $destination_command";
303             }
304              
305             package IPC::Open3::Callback::Command::CommandOptions;
306             $IPC::Open3::Callback::Command::CommandOptions::VERSION = '1.16';
307 1     1   418 use parent qw(Class::Accessor);
  1         280  
  1         4  
308             __PACKAGE__->follow_best_practice;
309             __PACKAGE__->mk_ro_accessors(qw(hostname pretty ssh sudo_username username));
310              
311             sub new {
312             my ( $class, @args ) = @_;
313             return bless( {}, $class )->_init(@args);
314             }
315              
316             sub clone {
317             my ( $instance, %options ) = @_;
318             if ( exists( $instance->{hostname} ) && !exists( $options{hostname} ) ) {
319             $options{hostname} = $instance->{hostname};
320             }
321             if ( exists( $instance->{ssh} ) && !exists( $options{ssh} ) ) {
322             $options{ssh} = $instance->{ssh};
323             }
324             if ( exists( $instance->{username} ) && !exists( $options{username} ) ) {
325             $options{username} = $instance->{username};
326             }
327             if ( exists( $instance->{sudo_username} ) && !exists( $options{sudo_username} ) ) {
328             $options{sudo_username} = $instance->{sudo_username};
329             }
330             if ( exists( $instance->{pretty} ) && !exists( $options{pretty} ) ) {
331             $options{pretty} = $instance->{pretty};
332             }
333             return new( ref($instance), %options );
334             }
335              
336             sub _init {
337             my ( $self, %options ) = @_;
338              
339             $self->{hostname} = $options{hostname} if ( defined( $options{hostname} ) );
340             $self->{ssh} = $options{ssh} if ( defined( $options{ssh} ) );
341             $self->{username} = $options{username} if ( defined( $options{username} ) );
342             $self->{sudo_username} = $options{sudo_username} if ( defined( $options{sudo_username} ) );
343             $self->{pretty} = $options{pretty} if ( defined( $options{pretty} ) );
344              
345             return $self;
346             }
347              
348             1;
349              
350             __END__