Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't system(@array) work when @array contains a find command?

Tags:

perl

I'm beginning to love Perl, but I don't understand whats going on here.

why does this work

my @cmd = ();
push @cmd, 'find';
push @cmd, 'data/path/';
push @cmd, "-name";
push @cmd, '*.xml';
push @cmd, '-exec';
push @cmd, 'mv';
push @cmd, '{}';
push @cmd, 'junk/path/';
push @cmd, '\;';    
say join (' ', @cmd);       
system(join(' ', @cmd));

Output

find data/path/ -name *.xml -exec mv {} junk/path/ \;

No errors from find! While this does not work

my @cmd = ();       
push @cmd, 'find';
push @cmd, 'data/path/';
push @cmd, "-name";
push @cmd, '*.xml';
push @cmd, '-exec';
push @cmd, 'mv';
push @cmd, '{}';
push @cmd, 'junk/path/';
push @cmd, '\;';    
say join (' ', @cmd);       
system(@cmd);

Output:

find data/path/ -name *.xml -exec mv {} junk/path/ \;
find: Missing argument for »-exec«.

system should be able to understand arrays. See here. When I copy the output into shell, there is no missing argument, it just works. But my script can't execute this.

like image 721
Powerriegel Avatar asked Aug 11 '26 00:08

Powerriegel


2 Answers

In the second system call, you shouldn't escape the ;.

The -exec switch to find slurps arguments until it finds a ;. ; is interpreted by the shell as a command separator, so when you run find ... -exec from the command-line, you need to escape it, and in shell scripts you'll see the pattern

find ... -exec ... \;

In your second system call, you collect all of the arguments in an array and pass the array directly to system. In this case, Perl is not using the shell to interpret the command, and the find command sees the argument \; instead of ;, and the -exec switch gets confused.

All you need to say to get your system command to work is

...
push @cmd, ';';
...
system(@cmd);
like image 72
mob Avatar answered Aug 14 '26 15:08

mob


mob pointed out the immediate problem with your system call, but there's a better way to do this. Instead of executing an external command, you can use File::Find::Rule, which has an interface that's very similar to the find utility:

use strict;
use warnings 'all';

use File::Copy;
use File::Find::Rule;

my $dest = '/foo/bar';

File::Find::Rule->file
                ->name('*.xml')
                ->exec( sub { move($_, $dest) or warn "move($_, $dest) failed: $!" } )
                ->in('.');
like image 37
ThisSuitIsBlackNot Avatar answered Aug 14 '26 15:08

ThisSuitIsBlackNot



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!