system
, exec
, open '|-'
, open2
, etc. all allow me to specify the command to run as a list of arguments that will be passed directly to execvp
instead of run through a shell.
Even if perl
is smart enough to run it directly if it looks like a "simple" command, that saves me the trouble of correctly shell-escaping the arguments with all the nasty pitfalls that it entails.
Example:
open my $out, '|-', $prog, @args;
system $prog, @args;
exec $prog, @args;
instead of
open my $out, "|$prog @args";
system "$prog @args";
exec "$prog @args";
Is there such an equivalent for the qx//
operator? Or do you have to always do it by hand eg.
sub slurpcmd {
open my $h, '-|', @_ or die "open $_[0]|: $!";
local $/ unless wantarray;
<$h>
}
A list form of the qx operator is provided by the module IPC::System::Simple as the function capturex
(additionally like the other functions in that module, it will throw an exception if there is an execution error or non-zero response code, which you can tweak). Alternatively, you can use Capture::Tiny to wrap a core system
call and provide the same behavior, but it also has other functions that can wrap STDERR together or separately from STDOUT.
use strict;
use warnings;
use IPC::System::Simple 'capturex';
my $output = capturex $prog, @args;
use Capture::Tiny 'capture_stdout';
my ($output, $exit) = capture_stdout { system $prog, @args };
# standard system() error checking required here
In core the pipe open is for the most part the only option, aside from IPC::Open3 which is similarly complex but allows directing STDERR as well.
Here are a few simple options.
String::ShellQuote + qx
:
use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote(@cmd);
my $output = `$cmd`;
IPC::System::Simple:
use IPC::System::Simple qw( capturex );
my $output = capturex(@cmd)
IPC::Run3:
use IPC::Run3 qw( run3 );
run3(\@cmd, \undef, \my $output);
IPC::Run:
use IPC::Run qw( run );
run(\@cmd, \undef, \my $output);
The first solution involves a shell, but none of the others.
It turns out that (unfortunately) this wasn't an overlook from my part -- the only solution really is to do it with open -|
or use one of the external modules listed in the other answers.
The backtick implementation (whether invoked by qx/.../
, `...`
, or readpipe
) is deep down hardwired to accept a single string argument:
PP(pp_backtick)
{
dSP; dTARGET;
PerlIO *fp;
const char * const tmps = POPpconstx;
const U8 gimme = GIMME_V;
const char *mode = "r";
TAINT_PROPER("``");
if (PL_op->op_private & OPpOPEN_IN_RAW)
mode = "rb";
else if (PL_op->op_private & OPpOPEN_IN_CRLF)
mode = "rt";
fp = PerlProc_popen(tmps, mode);
...
Notice the POPpconstx
which pops a single argument from the stack and the use of PerlProc_popen
instead of PerlProc_popen_list
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With