2008-10-16 2 views
2

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

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

perl try.pl MODULE1 Display 

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

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

Честно говоря, есть, вероятно, лучший способ решить вашу проблему, чем передавать имя модуля и работать как текст. На самом деле, возможно, есть много способов. Может быть, если мы увидим аргументы в пользу этого проектного решения, может возникнуть лучшее решение. – 2008-10-17 08:46:25

ответ

7

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

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

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

eval "use $package; 1" or die [email protected]; 

$package->$function(); 

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

4

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

perl -Mmodule -e function 
+0

Это работает только в том случае, если соответствующий модуль экспортирует «функцию» в основное пространство имен по умолчанию. Если это необязательный экспорт, вам нужно будет использовать -Mmodule = function, и если он вообще не экспортируется, вам придется использовать полностью квалифицированное имя или вызов метода. – friedo 2008-10-16 17:09:15

9

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

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

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

eval "use $package(); ${package}::$function()"; 
die [email protected] if [email protected]; 

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

+1

Injection - это проблема только в том случае, изменяясь как часть выполнения (например, по сети, например, сценарию CGI). Из командной строки это не проблема, потому что она все еще работает как вы. – Tanktalus 2008-10-16 17:21:49

2

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

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

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

Если она экспортирует ВТОРОСТЕПЕННУЮ все время (в @EXPORT), то Леон будет работать:

perl -MMyModule -e doit 

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

perl -MMyModule=doit -e doit 

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

3

Если вы хотите, чтобы ваш скрипт 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

Всегда начать свой Perl, как это:

use strict; 
use warnings 'all'; 

Затем выполните следующие действия:

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

Независимо от того, что вы делаете, старайтесь избегать «$ string».

2

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

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 
+0

Подумайте о детях ... как насчет создания этого `fermat c:`: `ekho halt – ysth 2008-10-17 06:41:02

1

Я хотел бы использовать UNIVERSAL::require. Это позволяет вам требовать или использовать модуль из переменной. Так что ваш код изменится на что-то вроде этого:

use UNIVERSAL::require; 

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

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

Смежные вопросы