32

我之前曾问过这个问题,或者搜索并看到其他人问过 - 为什么我会收到警告“ Subroutine mySub redefined at ../lib/Common.pm line x ”?并且您总是得到您在同一代码中两次声明 sub的答案。我创建了这个测试包:

整个文件---------------

package MyCommonPkg;

use strict;

sub thisSubroutineIsNotDefinedAnywhereElse{
}

1;

整个文件---------------

我使用 perl 脚本中的这个包,它使用其他包,也使用这个包,我收到警告:

子例程 ThisSubroutineIsNotDefinedAnywhereElse 在 ../lib/MyCommonPkg.pm 第 19 行重新定义。

我保证我没有在其他任何地方声明这个子。那么这是由循环引用引起的吗?我该如何着手跟踪并修复此警告的原因?

4

9 回答 9

39

你有依赖循环吗?如果 Perl 开始编译你的脚本并遇到这样的一行:

use PackageA;

Perl 暂停脚本的编译;找到 PackageA.pm 并开始编译它。如果遇到这样的一行:

use PackageB;

Perl 暂停 PackageA 的编译;找到 PackageB.pm 并开始编译它。通常,这将成功完成,Perl 将返回完成编译 PackageA,当它成功完成时,它会返回编译你的脚本,当它成功完成时,它会开始执行编译的操作码。

但是,如果 PackageB.pm 包含此行:

use PackageA;

由于 Perl 已经处理了 PackageA.pm,您可能会认为它不会做任何事情,但问题是它还没有完成。所以 Perl 会暂停 PackageB 的编译,重新从头开始编译 PackageA.pm。这可能会触发您看到的有关重新定义 PackageA 中的子例程的消息。

作为一般规则,两个包不应该都相互依赖。然而,有时循环更难定位,因为它是由第三个包引起的。

于 2010-08-07T05:37:35.700 回答
23

当您在不同的包中有两个具有相同名称的子例程时,您应该将此警告(启用警告时)视为“重新定义子例程......”。一个简单的原因(这与格兰特麦克莱恩所说的非常接近,但仍然不完全是)是你必须让你的包跳过编译阶段然后 make 然后 require。这样,Perl 命名空间管理器将不会在编译时找到任何此类具有相同名称的冲突符号,并且如果您的模块没有任何错误,它们之后也可以正常工作。

只要确保你实施

需要模块;

声明而不是

使用模块;

您不应再次看到此警告。

于 2012-08-10T22:58:31.623 回答
8

如果您使用的系统具有不区分大小写的文件系统(Windows,通常是 OSX),并且您use Common在一个文件和use common另一个文件中执行此操作,则可能会导致此类问题。

于 2010-08-07T00:33:41.117 回答
7

这听起来像是由循环依赖引起的问题。以下是如何追踪它。如果您的问题类如下所示:

package AlientPlanet;
use Dinosaurs;
sub has_dinosaurs {...}
1;

然后将您的示例更改为如下所示:

package AlienPlanet;
sub has_dinosaurs {...}     # <-- swap
use Dinosaurs;              # <-- swap
1;

现在用Carp::Always像这样编译你的代码:

⚡ perl -MCarp::Always -c lib/AlienPlanet.pm                                                                                                            
Subroutine has_dinosaurs redefined at lib/AlienPlanet.pm line 4.
    require AlienPlanet.pm called at lib/Dinosaurs.pm line 4
    Dinosaurs::BEGIN() called at lib/AlienPlanet.pm line 4
    eval {...} called at lib/AlienPlanet.pm line 4
    require Dinosaurs.pm called at lib/AlienPlanet.pm line 5
    AlienPlanet::BEGIN() called at lib/AlienPlanet.pm line 4
    eval {...} called at lib/AlienPlanet.pm line 4
lib/AlienPlanet.pm syntax OK

现在你有了一个堆栈跟踪,你可以看到循环在哪里。快速而肮脏的解决方案是在 Dinosaurs.pm中使用Class::Load 。

有关更详细的解释,请尝试我的博客文章

于 2013-01-20T21:54:04.577 回答
2

您是否有机会在网络服务器上将其作为 cgi 脚本运行?

我发现我需要重新启动网络服务器来绕过这个警告。

于 2011-02-27T15:43:09.073 回答
1

看看程序package MyCommonPkg.pm,看看它说了什么。它有这样的东西吗?

package MyCommonPkg;

use Exporter qw(import);   #  Might be "require" and not "use"
our @EXPORT = qw(thisSubroutineIsNotDefinedAnywhereElse);

语法可能有点不同。您应该看到的主要内容是package语句,它正在使用Exporter并且@EXPORT数组中包含您的子例程的名称。

正在发生的是命名空间冲突。您的包正在定义您正在定义的相同子例程。

为了防止这种情况发生,Perl 使用了命名空间。默认情况下,您的命名空间是main. package但是,假设包使用该命令定义自己的单独同名。

子例程或变量的完整名称空间是名称空间后跟一个双冒号,然后是子例程或变量名称。例如,如果您查看File::Find,您将看到对变量$File::Find::name和的引用$File::Find::dir。这些是变量$name和名称空间下的包$dir内。File/Find.pmFile::Find

为了让您更轻松,包可以它们的变量和子例程导出到您的命名空间中。例如,如果我使用File::Copy,O 可以这样做:

...
use File::Copy
...
copy ($file, $to_dir);

代替:

...
use File::Copy
...
File::Copy::copy ($file, $to_dir);

如果您查看File/Copy.pm,您将看到以下内容:

package File::Copy;
...
our(@ISA, @EXPORT, @EXPORT_OK, $VERSION, $Too_Big, $Syscopy_is_copy);
...
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(copy move);

package File::Copy;定义了一个命名空间。和允许包require Exporter;@ISA = qw(Exporter)子例程和变量导出到命名空间。@EXPORT自动将andcopymove例程导入到 命名空间,不管你是否想要它们!

最后一点非常重要。它现在被认为是不礼貌的使用@EXPORT。相反,您应该使用@EXPORT_OKwhich 要求您列出要使用的子例程。像Scalar::Util这样的更现代的软件包可以做到这一点。

这么几件事。首先,你MyCommonPkg有没有package MyCommonPkg;声明。如果没有,它应该。这可以防止包子例程和变量以讨厌的方式影响您的程序。然后,您可以使用@EXPORT@EXPORT_OK

如果MyCommonPkg确实有一个package声明,它是否使用@EXPORT?如果是这样,您有几种方法可以避免此问题:

  • 忽略警告。这只是一个警告。既然您知道您正在重新定义子程序,并且您想使用您对子程序的定义,请忽略它。

您可以这样做以在重新定义子例程时关闭警告:

use MyCommonPkg;

no warnings qw(redefine);
sub thisSubroutineIsNotDefinedAnywhereElse {
   ...
}
use warnings qw(redefine);
  • 使用require MyCommonPkg;而不是use MyCommonPkg;. 这将防止将任何子例程或变量导入您的命名空间,包括您想要使用的那些。假设MyCommonPkg定义了四个子例程:thisSubroutineIsNotDefinedAnywhereElsefoobarbarfoo。使用这些子程序中的任何一个。

你需要这样做:

my $answer = MyCommonPkg::foo( $input );

不好玩。

  • 为您的子例程使用另一个名称。应该已经记录了该子例程是在 中定义的MyCommonPkg,并且如果要使用MyCommonPkg,则不应使用导出的子例程名称。

  • 最后,如果MyCommonPkg是相当新的,并且没有在几十个程序中使用,使用@EXPORT_OK代替@EXPORT,并确保所有使用的程序MyCommonPkg都被修改以导出他们想要的子程序:

像这样:

use MyCommonPkg qw(foo bar);

在这种情况下,只有子程序foobar被导出。子例程thisSubroutineIsNotDefinedAnywhereElsebarfoo不会导出到您的环境中。

于 2013-10-27T17:04:08.920 回答
0

我尝试使用“package Common.pm”作为包名。编译器给了我错误。很亲切吧?你用的是什么版本的 Perl?我在 5.10.0 和 5.12.1 上试过。

即使您可以编译,最好删除 .pm 文件。例如;

文件:some_package.pm;

package some_package;
use strict;

sub yadayadayada { ... }

1;
于 2010-08-07T01:51:00.413 回答
0

确保您没有忘记模块末尾的这一行:

1;

我知道它已包含在此处的一些示例中,但我之所以提到它是因为它很容易被忽视,而在我的情况下,它原来是错误的唯一原因!

于 2012-05-01T23:16:52.230 回答
0

我有同样的问题; 这是因为程序使用了一个模块,并且子程序同时存在于程序和 perl 模块中;

于 2017-08-22T17:19:08.067 回答