Reputation: 369
I need to call the system command using the form:
system( $cmd, @args );
When I define @args as
my @args = ( "input1", "input2", ">", "file.out" );
The ">" and "file.out" are not interpreted as I'd hoped. How can I send the output of this form of system command to a file?
Upvotes: 1
Views: 1722
Reputation: 386706
That passes four arguments to the program, as if you executed the following in the shell:
prog "input1" "input2" ">" "file.out"
You can't instruct the shell to redirect output without using a shell!
The following solutions assume:
my $prog = 'cat';
my @args = ( 'input1', 'input2' );
my $out_qfn = 'file.out';
The following solutions are all lacking some error checking.
Use the shell to perform the redirection and escaping.
system('/bin/sh', '-c', '"$@" > "$0"', $out_qfn, $prog, @args);
Use the shell to perform the redirection, and use Perl to perform the escaping.
use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote($prog, @args) . " >".shell_quote($out_qfn);
system('/bin/sh', '-c', $cmd);
The last line simplifies to
system($cmd);
Avoid using a shell. Use Perl to perform the redirection.
# This isn't safe if @args is empty.
open(my $out_fh, '>', $out_qfn)
or die("Can't create output file \"$out_qfn\": $!\n");
open(my $pipe, '-|', $prog, @args)
or die $!;
while (<$pipe>) {
print($out_fh $_);
}
close($fh);
or
# This isn't safe if @args is empty.
use IPC::Open3 qw( open3 );
{
open(local *CHILD_STDIN, '<', '/dev/null')
or die $!;
open(local *CHILD_STDOUT, '>', $out_qfn)
or die("Can't create output file \"$out_qfn\": $!\n");
my $pid = open3('<&CHILD_STDIN', '>&CHILD_STDOUT', '>&STDERR', $prog, @args);
waitpid($pid, 0);
}
or
use IPC::Run3 qw( run3 );
run3([ $prog, @args ], \undef, $out_qfn);
or
use IPC::Run qw( run );
run([ $prog, @args ], \undef, $out_qfn);
Upvotes: 8
Reputation: 53508
This is because > file.out
is a shell feature. By using system
in the way that you are - you're bypassing the shell, and feeding the arguments directly to the program you're calling.
Note that argument processing varies depending on the number of arguments. If there is more than one argument in LIST, or if LIST is an array with more than one value, starts the program given by the first element of the list with arguments given by the rest of the list. If there is only one scalar argument, the argument is checked for shell metacharacters, and if there are any, the entire argument is passed to the system's command shell for parsing (this is /bin/sh -c on Unix platforms, but varies on other platforms). If there are no shell metacharacters in the argument, it is split into words and passed directly to execvp , which is more efficient. On Windows, only the system PROGRAM LIST syntax will reliably avoid using the shell; system LIST , even with more than one element, will fall back to the shell if the first spawn fails.
And thus the redirect doesn't work - presumably your program is ignoring or otherwise dealing with arguments passed of >
and file.out
.
You can either do a one line "system":
system ( "$cmd @args" );
Or use open
to open a filehandle, and do the IO within your program.
Upvotes: 0