views:

2310

answers:

8

Requirement is to pass module name and function name from the command-line argument. I need to get the command-line argument in the program and I need to call that function from that module

For example, calling a try.pl program with 2 arguments: MODULE1(Module name) Display(Function name)

 perl try.pl MODULE1 Display

I want to some thing like this, but its not working, please guide me:

use $ARGV[0];
& $ARGV[0]::$ARGV[1]();
+6  A: 

There's many ways to do this. One of them is:

#!/usr/bin/perl
use strict;
use warnings;

my ( $package, $function ) = @ARGV;

eval "use $package; 1" or die $@;

$package->$function();

Note the the first argument of the function will be $package.

Yanick
+3  A: 

Assuming the module exports the function, this should do:

perl -Mmodule -e function
Leon Timmermans
That only works if the module in question exports "function" to the main namespace by default. If it's an optional export, you'd have to use -Mmodule=function, and if it's not exported at all, then you'd have to use a fully qualified name or method invocation
friedo
+6  A: 

Assuming the function is not a class method, try this:

#!/usr/bin/perl
use strict;
use warnings;

my ( $package, $function ) = @ARGV;

eval "use $package (); ${package}::$function()";
die $@ if $@;

Keep in mind that this technique is wide open to code injection. (The arguments could easily contain any Perl code instead of a module name.)

cjm
Injection is only a problem if there is user-changing as part of the execution (e.g., over the network, such as a CGI script). From the command-line, this isn't an issue because it's still all running as you.
Tanktalus
+2  A: 

As per Leon's, if the perl module doesn't export it, you can call it like so

perl -MMyModule -e 'MyModule::doit()'

provided that the sub is in that package.

If it exports the sub all the time (in @EXPORT), then Leon's will work:

perl -MMyModule -e doit

If it is an optional export (in @EXPORT_OK), then you can do it like this.

perl -MMyModule=doit -e doit

But the first will work in any case where the sub is defined to the package, and I'd probably use that one over the last one.

Axeman
+2  A: 

If you want to make sure your perl script is secure (or at least, prevent yourself from accidentally doing something stupid), I'd avoid doing any kind of eval on data passed in to the script without at least some kind of checking. But, if you're doing some kind of checking anyway, and you end up explicitly checking the input, you might as well explicitly spell out witch methods you want to call. You could set up a hash with 'known good' methods, thus documenting everything that you want callable and protecting yourself at the same time.

my %routines = (
    Module => {
        Routine1 => \&Module::Method,
        Routine2 => \&Module::Method2, 
    },
    Module2 => { 
        # and so on
    },
);

my $module  = shift @ARGV;
my $routine = shift @ARGV;

if (defined $module
    && defined $routine
    && exists $routines{$module}            # use `exists` to prevent 
    && exists $routines{$module}{$routine}) # unnecessary autovivication
{
    $routines{$module}{$routine}->(@ARGV); # with remaining command line args
}
else { } # error handling

As a neat side effect of this method, you can simply iterate through the methods available for any kind of help output:

print "Available commands:\n";
foreach my $module (keys %routines)
{
    foreach my $routine (keys %$module)
    {
        print "$module::$routine\n";
    }
}
Robert P
+1  A: 

Always start your Perl like this:

use strict;
use warnings 'all';

Then do this:

no strict 'refs';
my ($class, $method) = @_;
(my $file = "$class.pm") =~ s/::/\//g;
require $file;
&{"$class\::$method"}();

Whatever you do, try not to eval "$string" ever.

JDrago
+1  A: 

Well, for your revised question, you can do this:

use strict;
use warnings;

{
    no strict;
    use Symbol qw<qualify>;
    my $symb = qualify( $ARGV[1], $ARGV[0] );
    unless ( defined &{$symb} ) { 
        die "&$ARGV[1] not defined to package $ARGV[0]\::";
    }
    &{$symb};
}

And because you're specifying it on the command line, the easiest way to include from the command line is the -M flag.

perl -MMyModule try.pl MyModule a_subroutine_which_does_something_cool

But you can always

eval "use $ARGV[0];";

But that's highly susceptible to injection:

perl try.pl "Carp; `do something disastrous`;" no_op
Axeman
Think of the children...how about making that `fermat c:` : `ekho halt
ysth
A: 

I'd use UNIVERSAL::require. It allows you to require or use a module from a variable. So your code would change to something like this:

use UNIVERSAL::require;

$ARGV[0]->use or die $UNIVERSAL::require::ERROR;
$ARGV[0]::$ARGV[1]();

Disclaimer: I did not test that code and I agree Robert P's comment about there probably being a better solution than passing these as command line arguments.

gpojd