55

我有依赖于Term::ReadKey获取终端宽度的 Perl 代码。我的安装缺少这个模块,所以如果模块不存在,我想提供一个默认值而不是抛出异常。

我如何有条件地使用可选模块,而不提前知道它是否可用。

# but only if the module is installed and exists
use Term::ReadKey;
...

我怎样才能做到这一点?

4

8 回答 8

97

这是一个不需要其他模块的简单解决方案:

my $rc = eval
{
  require Term::ReadKey;
  Term::ReadKey->import();
  1;
};

if($rc)
{
  # Term::ReadKey loaded and imported successfully
  ...
}

请注意,下面所有使用的答案(我希望它们低于这个答案!:-)eval { use SomeModule }都是错误的,因为use语句是在编译时评估的,无论它们出现在代码中的什么位置。因此,如果SomeModule不可用,脚本将在编译后立即死亡。

use语句的字符串 eval 也可以工作 ( ),但是当/对做同样的事情eval 'use SomeModule';时,在运行时解析和编译新代码是没有意义的,并且在编译时进行语法检查以启动。)requireimport

最后,请注意,我在此处使用eval { ... }and$@是为了本示例的目的而简洁。在实际代码中,您应该使用Try::Tiny之类的东西,或者至少要注意它解决的问题

于 2008-10-30T21:07:56.050 回答
11

查看 CPAN 模块Module::Load::Conditional。它会做你想做的事。

于 2008-10-30T20:53:46.890 回答
7

经典的答案(至少可以追溯到 Perl 4,早在“使用”出现之前)是“require()”一个模块。这是在脚本运行时执行的,而不是在编译时执行的,您可以测试成功或失败并做出适当的反应。

于 2008-10-30T21:23:59.000 回答
4

如果您需要特定版本的模块:

my $GOT_READKEY;
BEGIN {
    eval {
        require Term::ReadKey;
        Term::ReadKey->import();
        $GOT_READKEY = 1 if $Term::ReadKey::VERSION >= 2.30;
    };
}


# elsewhere in the code
if ($GOT_READKEY) {
    # ...
}
于 2008-11-04T10:35:48.817 回答
4
if  (eval {require Term::ReadKey;1;} ne 1) {
# if module can't load
} else {
Term::ReadKey->import();
}

或者

if  (eval {require Term::ReadKey;1;}) {
#module loaded
Term::ReadKey->import();
}

注意:只有在正确加载的情况下1;才会执行。require Term::...

于 2009-05-05T06:43:47.767 回答
3
use Module::Load::Conditional qw(check_install);

use if check_install(module => 'Clipboard') != undef, 'Clipboard'; # class methods: paste, copy

使用if pragma 和Module::Load::Conditional核心模块。

check_install返回 hashref 或undef.


该模块也在编译指示文档的另见部分中提到:

Module::Load::Conditional provides a number of functions you can use to query what modules are available, and then load one or more of them at runtime.

于 2021-01-07T10:45:33.727 回答
0

这是加载可选模块的有效习惯用法(只要您没有在带有 sigdie 处理程序的代码库中使用它),

use constant HAS_MODULE => defined eval { require Module };

如果可用,这将需要模块,并将状态存储在常量中。

你可以这样使用,

use constant HAS_READLINE => defined eval { require Term::ReadKey };

my $width = 80;
if ( HAS_READLINE ) {
  $width = # ... code, override default.
}

请注意,如果您需要导入它并引入符号,您也可以轻松地做到这一点。你可以跟进。

use constant HAS_READLINE => defined eval { require Term::ReadKey };
Term::ReadKey->import if HAS_READLINE;

此方法使用常量。这样做的好处是,如果您没有此模块,则会从 optree 中清除死代码路径。

于 2020-11-20T01:49:20.057 回答
-1

我认为使用变量时它不起作用。请检查这个链接,它解释了它如何与变量一起使用

$class = 'Foo::Bar';
        require $class;       # $class is not a bareword
    #or
        require "Foo::Bar";   # not a bareword because of the ""

require 函数将在@INC 数组中查找“Foo::Bar”文件,并会抱怨在那里找不到“Foo::Bar”。在这种情况下,您可以这样做:

 eval "require $class";
于 2014-10-23T00:45:28.530 回答