【问题标题】:How do I interpolate variables to call a Perl function from a module?如何插入变量以从模块中调用 Perl 函数?
【发布时间】:2008-10-16 15:37:03
【问题描述】:

要求是从命令行参数传递模块名称和函数名称。 我需要在程序中获取命令行参数,并且需要从该模块调用该函数

例如,调用带有 2 个参数的 try.pl 程序:MODULE1(Module name) Display(Function name)

 perl try.pl MODULE1 Display 

我想要这样的东西,但它不起作用,请指导我:

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

【问题讨论】:

  • 老实说,可能有比将模块名称和函数作为文本传递更好的方法来解决您的问题。事实上,可能有很多方法。也许如果我们看到这个设计决策背后的原因,就会出现更好的解决方案。

标签: perl variables interpolation


【解决方案1】:

假设函数不是类方法,试试这个:

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

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

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

请记住,这种技术对代码注入非常开放。 (参数可以很容易地包含任何 Perl 代码而不是模块名称。)

【讨论】:

  • 只有在执行过程中存在用户更改(例如,通过网络,如 CGI 脚本)时,注入才是问题。从命令行来看,这不是问题,因为它仍然像你一样运行。
【解决方案2】:

有很多方法可以做到这一点。其中之一是:

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

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

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

$package->$function();  

注意函数的第一个参数是$package。

【讨论】:

    【解决方案3】:

    假设模块导出函数,应该这样做:

    perl -Mmodule -e function
    

    【讨论】:

    • 只有在有问题的模块默认将“function”导出到主命名空间时才有效。如果是可选导出,则必须使用 -Mmodule=function,如果根本不导出,则必须使用完全限定名称或方法调用
    【解决方案4】:

    如果你想确保你的 perl 脚本是安全的(或者至少,防止自己不小心做一些愚蠢的事情),我会避免对传入脚本的数据进行任何类型的 eval 至少没有某种检查。但是,如果您无论如何都在进行某种检查,并且最终明确地检查了输入,那么您不妨明确地拼出您想要调用的女巫方法。您可以使用“已知良好”方法设置散列,从而记录您想要调用的所有内容并同时保护自己。

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

    【讨论】:

      【解决方案5】:

      按照 Leon 的说法,如果 perl 模块导出它,你可以这样称呼它

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

      前提是子组件在那个包中。

      如果它一直导出子 (@EXPORT),那么 Leon's 将起作用:

      perl -MMyModule -e doit
      

      如果是可选导出(在@EXPORT_OK),那么你可以这样做。

      perl -MMyModule=doit -e doit
      

      但是第一个在 sub 被定义到包的任何情况下都可以工作,我可能会在最后一个上使用那个。

      【讨论】:

        【解决方案6】:

        总是这样启动你的 Perl:

        use strict;
        use warnings 'all';
        

        然后这样做:

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

        无论你做什么,都尽量不要评估“$string”。

        【讨论】:

          【解决方案7】:

          好吧,对于你的修订问题,你可以这样做:

          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
          

          【讨论】:

          • 想想孩子们......如何让fermat c: : `ekho halt
          【解决方案8】:

          我会使用UNIVERSAL::require。它允许您从变量中获取或使用模块。所以你的代码会变成这样:

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

          免责声明:我没有测试该代码,我同意 Robert P 的评论,即可能有比将这些作为命令行参数传递更好的解决方案。

          【讨论】:

            猜你喜欢
            • 2011-04-06
            • 2020-01-10
            • 2022-12-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-02-20
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多