用Perl重新加载模块并重新定义子例程

问题描述:

我目前正在尝试重新加载模块。我希望实现的目标是能够在模块文件中定义的子例程中更改某些内容,然后使用新定义重新加载该模块。用Perl重新加载模块并重新定义子例程

目前,我正在更改test子例程中的打印语句,以在等待子程序执行原始代码并在重新加载模块之前打印“这是一些不同的文本”。

然而,目前有什么我得到的是消息:
Subroutine test redefined at /Test/testmodule.pm line 9.

这正是我想要的,但输出如下。

this is some text 
Subroutine test redefined at /Test/testmodule.pm line 9. 
this is some text 

就是我希望的是,当模块被重新加载,并认识到子程序已经被重新定义,是,下一次执行测试例程,它会引用新的定义,而不是旧的。

我已经通过以前关于重新加载模块的问题进行了搜索,但给出的答案是循环依赖(包A使用B,B使用A)或包中的名称空间冲突,但这不是问题的关键这里。我想想要子程序被重新定义,并使用新的定义。

源代码: main.pl

#!/usr/bin/perl 
use strict; 
use warnings; 
use Module::Reload::Selective; 
use Test::testmodule; 

while(1) { 
    test(); #run module's define subroutine 
    sleep(5); #stop terminal from being flooded too quickly 

    #Ensure that the module is reloaded 
    $Module::Reload::Selective::Options->{SearchProgramDir} = 1; 
    $Module::Reload::Selective::Options->{ReloadOnlyIfEnvVarsSet} = 0; 
    Module::Reload::Selective->reload(qw(Test::testmodule)); #reload! 
} 

源代码:testmodule.pm(在./Test/相对于main.pl

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

# allow exportation 
require Exporter; 
our @ISA = qw(Exporter); 
our @EXPORT = qw(test); 

sub test { 
    print("this is some text\n"); # this line is edited in the source file to 
            # 'print("this is some different text\n");' 
} 
1; 

任何指针或引用到教程将是辉煌的。事实上,如果答案不是非常简单,不直接告诉我答案会让我阅读你的建议材料,并获得整体更好的理解。

所有需要的CPAN模块已安装,并且我可以确认testmodule.pm正在成功写入更改后。

OS:科学的Linux CERN 6,内核版本2.6.32-131.4.1.el6.x86_64
的Perl:对于x86_64的Linux的线程多

内置v5.10.1(*)

非常感谢,
欧文。

+0

什么新的定义?你正在重新加载相同的子,获得相同的输出。 – TLP

+1

感谢您发表。在第二段中,我提到我在_testmodule.pm_中更改了一行源代码。我应该编辑帖子以使其更加明确。谢谢你提请我注意。 – OwenD

我不知道这是问题还是不是,但是您在模块中缺少package声明。这意味着testmain::test而不是Test::testmodule::test

是的,这是cjm的答案和我的组合。此代码的工作对我来说:

Test/testmodule.pm

package Test::testmodule; 

use strict; 
use warnings; 

# allow exportation 
require Exporter; 
our @ISA = qw(Exporter); 
our @EXPORT = qw(test); 

sub test { 
    print "this is some text, counter 1\n"; 
} 

1; 

main.pl

#!/usr/bin/perl 
use strict; 
use warnings; 
use Module::Reload::Selective; 
use Test::testmodule; 

while(1) { 
    test(); #run module's define subroutine 

    my $module = do { 
     open my $fh, "<", "Test/testmodule.pm" 
      or die "could not open the module: $!"; 

     local $/; 
     <$fh>; 
    }; 

    $module =~ s/counter ([0-9])/"counter " . ($1 + 1)/e; 

    open my $fh, ">", "Test/testmodule.pm" 
     or die "could not open the module: $!"; 

    print $fh $module; 

    close $fh; 

    #Ensure that the module is reloaded 
    $Module::Reload::Selective::Options->{SearchProgramDir} = 1; 
    $Module::Reload::Selective::Options->{ReloadOnlyIfEnvVarsSet} = 0; 
    Module::Reload::Selective->reload(qw(Test::testmodule)); 
    Test::testmodule->import; 
} continue { 
    sleep 1; 
} 

为了澄清,当你创建一个.pm文件的Perl 5不会创建一个命名空间。当你说package NamespaceName它创建了一个命名空间或您引用该命名空间这样

sub Test::testmodule::test { 
    print "this is some text, counter 1\n"; 
} 

由于您版本test功能在该Test::testmodule命名空间是没有,它从来没有得到重新加载。

+0

啊,我忘了包声明......它总是**一天结束的时候真的很简单... 谢谢你的回答,虽然它有很多帮助。 编辑:它也适用于我直接修改文件,而不是通过代码。谢谢(和cjm)非常感谢您的帮助! – OwenD

您可以通过将

no warnings 'redefine'; 

到加载模块停止发出警告。

另外请注意,重新加载是相当脆弱的概念,它可能会限制你可以在你的模块中做什么。

+1

感谢您的意见。但是,警告不是我的问题。问题是使用子例程的新定义。 – OwenD

您错过了Module::Reload::Selective文档中说重新加载后调用import的部分。 (虽然它使用的间接对象语法,它更好地使用标准方法调用。)也就是说,你应该说:

Module::Reload::Selective->reload(qw(Test::testmodule)); #reload! 
Test::testmodule->import;        #reimport! 

的原因是,Exporter本质上是做:

*main::test = \&Test::testmodule::test; 

那是main::test被指定为的当前版本Test::testmodule::test。重新加载模块重新定义Test::testmodule::test,但main::test继续引用原始子。明确调用import方法会将新版本的副本复制到main::test

+1

非常感谢你的回答。不幸的是,虽然很有希望,但重新进口似乎没有任何区别。也许它与新重新导入的子程序有错误的范围有关?即该子程序只能作为'while'块的范围而不是整个文件? – OwenD