ernix ernix - 7 months ago 18
Perl Question

Is it safe using $Config{perlpath} in system/exec/open?

Assume that I have following code to open filehandle:

open my $fh, "command1 | command2 |";


I found
command1
may output that
command2
can not handle well, so I'm trying to insert a perl filter between
command1
and
command2
to deal with them:

use Config;
open my $fh, "command1 | $Config{perlpath} -ple 'blah blah' | command2 |";


My questions are:


  1. Is it OK to use
    $Config{perlpath}
    in system call directly?

  2. Calling own perl binary seems nuts. Are there any better solutions?



Thanks

Answer

Is it OK to use $Config{perlpath} in system call directly?

Relatively. It's about as portable as the rest of your code (which already depends on running on something unix-ish). There's some security worry, but I'd say not a very large one because someone who can affect the value of that variable already has scope to cause havoc. $^X is probably safer in that regard. You might want to try quoting it using String::ShellQuote just for safety, since you can't bypass the shell in the midst of a pipeline.

Calling own perl binary seems nuts. Are there any better solutions?

Depends on your definition of "better". There's definitely another way around, which is to run both command1 and command2 separately, process command1's output in your original perl process, hand it to command2, and read command2's output. However, you have to be careful how you do it. There are two safe ways.

The first way is easier, but takes more time and memory: run command1 first, read and process all of its output into a string, then run command2 providing the buffered output as input. The best way to do this is to use IPC::Run to handle command2's input and output (and maybe both commands, just for consistency); if you try to just print all the data to command2's input handle and then read all the output you can deadlock, but IPC::Run will interleave reads and writes if necessary behind the scenes, and give you a nice easy interface.

The second way is more complicated but closer in behavior to the original: you need some kind of async framework like IO::Async or POE, using its classes for process construction, and set up handlers to communicate between them, do your filtering, and gather the output.

Here's a tested toy example of that (gist because it's a couple screenfuls of code) that does the equivalent of ls | perl -pe '$_ = uc' | rev, except with the middle part of the pipeline running in the parent perl process. You may never use it, but I thought it was worth illustrating.