LKramer LKramer - 1 month ago 12
Perl Question

Perl system command with multiple parameters output to file

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?

Answer

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.

Solution 1

Use the shell to perform the redirection and escaping.

system('/bin/sh', '-c', '"$@" > "$0"', $out_qfn, $prog, @args);

Solution 2

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);

Solution 3

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);