Using the right Perl array references with IPC::Run

1

I'm trying to get this Perl code to work correctly and I believe I just need to get my references right. I would appreciate any feedback on how to use references, but if this code can't be made to work without API changes I would also appreciate feedback on how to change the surrounding APIs to support the data types I need.

I'm trying to add a new test to the PostgreSQL test suite. The code in question runs a subprocess and stores the process arguments in an array literal in a hash. for example:

my %pgdump_runs = (
    defaults => {
        dump_cmd => [
            'pg_dump', '--no-sync',
            '-f',      "$tempdir/defaults.sql",
            'postgres',
        ],
    },
    # and others
)

That array gets passed to a helper library like so:

    $node->command_ok(\@{ $pgdump_runs{$run}->{dump_cmd} },
        "$run: pg_dump runs");

The helper library keeps passing the array down:

sub command_ok
{
    my ($cmd, $test_name) = @_;
    my $result = run_log($cmd);

and then finally calling IPC::Run:

sub run_log
{
    return IPC::Run::run(@_);
}

To write my test case I have to pipe the output of a command to a file descriptor that does not support seeking. It looks like the IPC::Run module supports piping for you. Here's the example for the docs:

run \@cmd1, '|', \@cmd2;

But I tried a bunch of different ways to try and structure my array literal and none of them worked. For example, I tried this and I believe it gave me an ARRAY(0xFFFFFFFF) error:

        dump_cmd => [
            ['pg_dump', 'db'],
            '|',
            ['pg_restore', 'db2'],
        ],

How can I get this code to set up multiple sub-processes with piping?

UPDATE: I tried out ikegami's suggestion: the command_ok function can only have two arguments passed to it, so that doesn't work (you have to pass an array reference for the first argument).

UPDATE: turning on IPC:Run's debug functionality I can see how it's trying to interpret my command line. Instead of dereferencing the array it's trying to stringify (?) it and exec that string as an executable:

1070 IPC::Run 0002 01234567890- [#11(59856)]: parsing [ 'ARRAY(0x7ff3016f1090)', '|', 'ARRAY(0x7ff3016f1510)', '>/Users/david/src/pg_bug/postgres/foo' ]
perl
perl-ipc-run
asked on Stack Overflow May 29, 2020 by ldrg • edited Jul 7, 2020 by ldrg

1 Answer

1

You are effectively doing the following:[1]

my %pgdump_runs = (
    foo => {
        dump_cmd => [
            ['pg_dump', 'db'],
            '|',
            ['pg_restore', 'db2'],
        ],
    },
);

IPC::Run::run( $pgdump_runs{foo}{dump_cmd} )

This passes a single argument to run, but you indicated you wanted to achieve something analogous to

run \@cmd1, '|', \@cmd2;

That would require

IPC::Run::run( @{ $pgdump_runs{foo}{dump_cmd} } )

A few ways you can fix this:

  1. Replace

    return IPC::Run::run(@_);
    

    with

    return IPC::Run::run(@{ $_[0] });
    
  2. Replace

    run_log($cmd);
    

    with

    run_log(@$cmd);
    
  3. Replace

    sub command_ok {
        my ($cmd, $test_name) = @_;
        my $result = run_log($cmd);
    }
    
    $node->command_ok(\@{ $pgdump_runs{$run}->{dump_cmd} }, "$run: pg_dump runs");
    

    with

    sub command_ok {
        my $test_name = shift;
        my $result = run_log(@_);
    }
    
    $node->command_ok("$run: pg_dump runs", @{ $pgdump_runs{$run}{dump_cmd} });
    
  4. Replace

    dump_cmd => [
        ['pg_dump', 'db'],
        '|',
        ['pg_restore', 'db2'],
    ],
    

    with

    use String::ShellQuote qw( shell_quote );
    
    dump_cmd => join(' ',
        shell_quote('pg_dump', 'db'),
        '|',
        shell_quote('pg_restore', 'db2'),
    ),
    

Footnotes:

  1. Unless you are trying to autovivify (which you're not),

    \@{ $pgdump_runs{$run}->{dump_cmd} }
    

    can also be written as

    $pgdump_runs{$run}->{dump_cmd}
    

    And in both cases, the -> can be omitted because it's between two indexes.

answered on Stack Overflow May 29, 2020 by ikegami • edited Jul 7, 2020 by ikegami

User contributions licensed under CC BY-SA 3.0