Running commands from perl

I think most people are aware of the dangers of running external commands from Perl, particularly when they involve variables storing user input. Over the years multiple methods have been provided to make things safer, such as system() taking a list of command parts and not using the shell rather than the original single string-based approach which forks a shell. That’s fine when you just want to fire off a command and check the return code, often though we want to capture the output for immediate perusal or store it directly into a file. Here’s a snippet of code from the subversion component I used when I wanted to run the svnadmin command and send the output direct to a file:

        my $out = IO::File->new( $outfile, 'w' ) 
              or $self->Fail("Could not open $outfile: $!");

        open( my $in, '-|', $svnadmin, 'dump', $repo_path )
            or $self->Fail("Failed to dump svn repository $repo_path: $!");

        while ( defined ( my $line =  ) ) {
            print {$out} $line;
        }

        $out->close or $self->Fail("Failed to save svn repository dump: $!");

All that just to do the equivalent of "svnadmin dump > /path/to/dumpfile". It could be even more complicated if you got an error code returned and you wanted to examine the output on stderr.

There are various solutions to this problem based around IPC::Open2 and IPC::Open3 but they can still be quite hairy and involve opening and reopening file handles.

I have recently discovered a much nicer, simpler interface which hides the complexity in solving these problems, the module is named IPC::Run. A quick example from the module summary shows the potential:

   ## First,a command to run:
      my @cat = qw( cat );

   ## Using run() instead of system():
      use IPC::Run qw( run timeout );

      run \@cmd, \$in, \$out, \$err, timeout( 10 ) or die "cat: $?"

      # Can do I/O to sub refs and filenames, too:
      run \@cmd, '<', "in.txt", \&out, \&err or die "cat: $?"
      run \@cat, '>', "out.txt", '2>>', "err.txt";


      # Redirecting using psuedo-terminals instad of pipes.
      run \@cat, '<ptypty>', \$out_and_err;

This is barely scratching the surface, it can act like Expect and do loads of other clever tricks.

Comments are closed.