Как интерполировать переменные для вызова функции Perl из модуля? - PullRequest
2 голосов
/ 16 октября 2008

Требуется передать имя модуля и имя функции из аргумента командной строки. Мне нужно получить аргумент командной строки в программе, и мне нужно вызвать эту функцию из этого модуля

Например, вызов программы try.pl с двумя аргументами: MODULE1 (имя модуля) Display (имя функции)

 perl try.pl MODULE1 Display 

Я хочу что-то подобное, но оно не работает, пожалуйста, наведите меня:

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

Ответы [ 8 ]

9 голосов
/ 16 октября 2008

Предполагая, что функция не является методом класса, попробуйте это:

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

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

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

Имейте в виду, что этот метод широко открыт для внедрения кода. (Аргументы могут легко содержать любой код Perl вместо имени модуля.)

7 голосов
/ 16 октября 2008

Есть много способов сделать это. Один из них:

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

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

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

$package->$function();  

Обратите внимание, что первым аргументом функции будет $ package.

4 голосов
/ 16 октября 2008

Предполагая, что модуль экспортирует функцию, это должно сделать:

perl -Mmodule -e function
3 голосов
/ 16 октября 2008

Если вы хотите убедиться, что ваш Perl-скрипт безопасен (или, по крайней мере, не допустите, чтобы вы случайно сделали что-то глупое), я бы избегал какого-либо анализа данных, передаваемых в сценарий, без хотя бы какого-либо проверка. Но, если вы все равно делаете какую-то проверку и в конечном итоге вы явно проверяете входные данные, вы также можете явно указать методы ведьмы, которые хотите вызвать. Вы можете создать хеш с помощью «известных хороших» методов, таким образом, документируя все, что вы хотите, вызываемое и одновременно защищая себя.

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

В качестве аккуратного побочного эффекта этого метода вы можете просто перебрать методы, доступные для любого вида вывода справки:

print "Available commands:\n";
foreach my $module (keys %routines)
{
    foreach my $routine (keys %$module)
    {
        print "$module::$routine\n";
    }
}  
2 голосов
/ 17 октября 2008

Ну, для вашего исправленного вопроса вы можете сделать это:

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

И поскольку вы указываете его в командной строке, самый простой способ включить его из командной строки - это флаг -M.

perl -MMyModule try.pl MyModule a_subroutine_which_does_something_cool

Но вы всегда можете

eval "use $ARGV[0];"; 

Но это очень восприимчиво к инъекции:

perl try.pl "Carp; `do something disastrous`;" no_op
2 голосов
/ 17 октября 2008

Всегда запускайте свой Perl так:

use strict;
use warnings 'all';

Тогда сделайте это:

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

Что бы вы ни делали, старайтесь никогда не набирать "$ string".

2 голосов
/ 16 октября 2008

Согласно Леону, если модуль perl не экспортирует его, вы можете назвать его так:

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

при условии, что саб находится в этом пакете.

Если он все время экспортирует саб (в @EXPORT), то Leon будет работать:

perl -MMyModule -e doit

Если это необязательный экспорт (в @EXPORT_OK), вы можете сделать это следующим образом.

perl -MMyModule=doit -e doit

Но первый будет работать в любом случае, когда подпрограмма определена для пакета, и я бы, вероятно, использовал ее над последним.

1 голос
/ 21 октября 2008

Я бы использовал УНИВЕРСАЛЬНЫЙ :: требуется . Это позволяет вам требовать или использовать модуль из переменной. Таким образом, ваш код изменится на что-то вроде этого:

use UNIVERSAL::require;

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

Отказ от ответственности: я не тестировал этот код, и я согласен с комментарием Роберта П. о том, что, вероятно, есть лучшее решение, чем передача их в качестве аргументов командной строки.

...