6

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>
}

3 Answers3

5

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.

Grinnz
  • 9,093
  • 11
  • 18
  • those modules are not part of the standard distribution, right? –  Feb 01 '19 at 22:34
  • @Căcărău IPC::System::Simple and Capture::Tiny are not core, but they are pure-perl so they can be [fatpacked](https://metacpan.org/pod/App::FatPacker) into a script. – Grinnz Feb 01 '19 at 22:34
  • @zdim It is functionally identical to qx except for the method in which it takes arguments and its additional error handling. Not to imply that it's another built-in operator, since it clearly isn't. – Grinnz Feb 02 '19 at 03:45
4

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.

ikegami
  • 367,544
  • 15
  • 269
  • 518
1

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.

  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/187828/discussion-on-answer-by-ccru-use-perls-qx-operator-with-a-list-of). Please [edit] the answer to incorporate any relevant information from the chat discussion. – Cody Gray - on strike Feb 04 '19 at 02:29