0

I would like some help figuring this particular coding problem.

I have a perl script (#1) that calls another perl script(#2). In #1, I call #2 to also redirect its output to a log file. like so

my @command = ('downloadImage', '-url', $url, '>', $log);

this commands runs fine when it runs on the terminal. Do I have to use some other kind of special character to tell perl that > is output redirection not just some regular character?

I run it as:

system(@command);
ikegami
  • 367,544
  • 15
  • 269
  • 518
Ria
  • 447
  • 6
  • 16

2 Answers2

4

I assume the full code is:

my @command = ('downloadImage', '-url', $url, '>', $log);
system @command;

system has two modes. system $command will run $command in the shell.

# This will write 'foo' to the file 'bar'
system "echo foo > bar";

system @command is really system $program, @args. It will bypass the shell and run the $program with @args.

# This will print 'foo > bar'
system "echo", "foo", ">", "bar"

So if you want to do output redirection like that you could join @command together.

system join " ", @command;

But that can run into shell quoting issues. It's safer, faster, and more portable to do the redirection yourself in Perl using a piped open.

use strict;
use warnings;
use autodie;

open my $echo, "-|", "echo", "foo";
open my $log, ">", "some.log";

print {$log} <$echo>;
ikegami
  • 367,544
  • 15
  • 269
  • 518
Schwern
  • 153,029
  • 25
  • 195
  • 336
3

Doing

system('downloadImage', '-url', $url, '>', $log)

in Perl is the same as doing

'downloadImage' '-url' "$url" '>' "$log"

in the shell. This executes downloadImage with four arguments, one of which is >. The shell command you were trying to execute is the following:

'downloadImage' '-url' "$url" > "$log"

If you want to execute that shell command, you first need a shell. The following is how you should achieve this:

use String::ShellQuote qw( shell_quote );

my @cmd = ('downloadImage', '-url', $url);

my $shell_cmd = shell_quote(@cmd) . ' >' . shell_quote($log);
system($shell_cmd);  # Short for system('/bin/sh', '-c', $shell_cmd);

However, there are lots of downsides to using a shell. Here are some alternatives:

use IPC::Open3 qw( open3 );

my @cmd = ('downloadImage', '-url', $url);

{
   open(local *CHILD_STDIN, '<', '/dev/null')
      or die("Can't open \"/dev/null\": $!\n");

   open(local *CHILD_STDOUT, '>', $log)
      or die("Can't create \"$log\": $!\n");

   my $pid = open3('<&CHILD_STDIN', '>&CHILD_STDOUT', '>&STDERR', @cmd);
   waitpid($pid, 0);
}

or

use IPC::Run3 qw( run3 );

my @cmd = ('downloadImage', '-url', $url);

run3(\@cmd, \undef, $log);

or

use IPC::Run qw( run );

my @cmd = ('downloadImage', '-url', $url);

run(\@cmd, '<', '/dev/null', '>', $log);
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • 1
    (I only included IPC::Open3 because it's bundled with Perl. It's honestly too low-level for most uses.) – ikegami Oct 21 '16 at 18:46