tags:

views:

379

answers:

4

In Perl, is it possible to make 'exec', 'system', and 'qx' use a shell other than /bin/sh (without using a construct like 'exec "$SHELL -c ..."', and without recompiling perl)?

EDIT: The motivation for this question is a bash script that does 'export -f foo' and then uses perl in a subshell to invoke the function directly via 'system "foo"'. I am not sure that this technique will work with all sh, and although 'system "/bin/bash -c foo"' may work in that scenario, I wouldn't expect the exported function to propagate through all variants of /bin/sh. But mostly I was just curious, and am now curious about how to extend the solution to qx. Also, since I know nothing about non-unix platforms, I'd like to avoid hard coding the path to an alternate shell in the solution.

A: 

exec doesn't use /bin/sh

It just execs the program you specify. No shells.

If you want it to go through a shell you have to do that yourself.

Kristoffon
Not true. Have a look at the `exec` entry in `perlfunc` manpage.
hillu
+3  A: 

exec and system will use the shell (which will likely not be /bin/sh on non-UNIX systems) if you only pass one argument to it. (Details are described in perlfunc)

You may want to have a look at IPC::Run3 as an alternative to system

hillu
+6  A: 

You can override exec and system. See perldoc perlsub for the details, but here is roughly what you want (modulo some quoting bugs I don't feel like trying to fix):

#!/usr/bin/perl

use strict;
use warnings;

use subs qw/system/;

sub system {
    #handle one arg version:
    if (@_ == 1) {
     return CORE::system "$ENV{SHELL} -c $_[0]";
    }
    #handle the multi argument version
    return CORE::system @_;
}

print "normal system:\n";
system "perl", "-e", q{system q/ps -ef | grep $$/};

print "overloaded system:\n";
system 'ps -ef | grep $$';
Chas. Owens
If the one argument contains spaces, doesn't the corresponding line need to deal with that - by quoting the $_[0] argument appropriately or something similar?
Jonathan Leffler
Yes, hence the "modulo some quoting bugs I don't feel like trying to fix".
Chas. Owens
This seems to me to be the same as not overriding system and exec and simply executing the desired shell. Instead of overriding it, possibly messing up other things, just wrap your call in your own named subroutine. There's no magic necessary.
brian d foy
+3  A: 

Why don't you want to use 'exec "$SHELL -c ..."'? If you don't want see that code every time you call exec or system, just hide it in a subroutine. That's what they're there for. :)

sub my_exec {
    exec $ENV{SHELL}, '-c', @_;
    }

If you want to do that, however, I suggest somehow sanitizing $ENV{SHELL} so that people don't do odd things to your script by setting weird values. You might want to ensure that the shell is listed in /etc/shells or whatever way your system lists approved login shells. You also need to do a bit more work to make this taint-clean, which you should probably do if you are going to send data to another process.

brian d foy