28

I have never fully understood Perl's resolution of package names, but I always assumed that the following should always work, assuming you are executing myscript.pl from within the directory that contains it:

myscript.pl (contains the following statement: use Class1::Class2::Class3)
Class1/
    Class2/
        Class3.pm (contains the following package declaration: package Class1::Class2::Class3;)

However, this is not working in my code because Class3.pm cannot be located. Looking at @INC, it does not include the current directory, only various directories of my Strawberry Perl installation.

What is the recommended way to solve this? I suppose I could modify @INC, or I could start using FindBin, but I'm not sure which is best. I have inherited this code and am simply migrating it to a new location, but it doesn't look like the old code needed either such solution (I could be wrong, still looking...)

ikegami
  • 367,544
  • 15
  • 269
  • 518
Stephen
  • 8,508
  • 12
  • 56
  • 96

3 Answers3

39

Perl has never searched the script's directory for modules.

Perl did search the current directory. It is because the current directory is sometimes the same as the script directory that it appeared that Perl searched the script's directory.

But the current directory is often different than the script's directory. So code like yours that assumes that Perl searches the script's directory for modules has always been buggy.

Since 5.26, Perl no longer searches the current directory for security reasons. This simply made the bug more evident.


To tell Perl to look in the script's directory for modules, use the following:

use FindBin 1.51 qw( $RealBin );
use lib $RealBin;

or

use Cwd qw( abs_path );
use File::Basename qw( dirname );
use lib dirname( abs_path( $0 ) );
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • A tangential question, but why `$RealBin` and not just `$Bin`? Does having the links resolved give us any benefit here, or have you used it here just a general good practice? – Sundar R Apr 09 '18 at 12:19
  • 1
    @sundar, `$Bin` won't work if someone creates a symlink to the script. – ikegami Apr 09 '18 at 12:26
  • What is the 1.51 argument to `FindBin`? I don't see anything like that usage mentioned in [perldoc for FindBin](http://perldoc.perl.org/FindBin.html). – Greg Kennedy Apr 11 '18 at 05:38
  • @Greg Kennedy, It's a version check. See the `use Module VERSION LIST` syntax of [`use`](http://perldoc.perl.org/functions/use.html). – ikegami Apr 11 '18 at 05:53
  • regarding your 2nd option, I've always used something similar: `use Cwd(); use File::Basename(); use lib Cwd::realpath(File::Basename::dirname(__FILE__));` Which is better: yours or mine? – textral Aug 29 '18 at 00:52
  • @textral, You have dirname and realpath inside out. Your way fails to handle symlinks – ikegami Aug 29 '18 at 00:54
  • Ah, thanks! From Cwd's "man page" I see that `realpath` is just an alias of `abs_path`. So what's the difference between `__FILE__` and `$0`? – textral Aug 29 '18 at 01:12
  • 2
    @textral, No difference between `__FILE__` and `$0` (unless you changed `$0`, so `__FILE__` is slightly better). That's not the problem. The problem is that you used `realpath(dirname(__FILE__))` instead of `dirname(realpath(__FILE__))`. Your way will fail if a symlink is used. – ikegami Aug 29 '18 at 06:17
11

Having . (the current directory) in @INC was removed in 5.26 for security reasons (CVE-2016-1238). Some Linux distributions have backported the change, so you might run into this problem even if you're using e.g. 5.24.

melpomene
  • 84,125
  • 8
  • 85
  • 148
  • @ikegami Oh, interesting. I didn't know FindBin was fixed. It did [search `$PATH`](https://metacpan.org/source/DAPM/perl-5.14.4/lib/FindBin.pm#L153) until version 1.51, released with perl 5.16. – melpomene Oct 03 '17 at 17:42
  • @ikegami I've retracted my code. Your `FindBin` solution is better (assuming it's at least version 1.51, so `use FindBin 1.51 qw($RealBin)` may be a good idea). – melpomene Oct 03 '17 at 17:45
7

Perl 5.26 removed having the current working directory in @INC as a security measure.

It's explained in the 5.26 perldelta notes.

stevieb
  • 9,065
  • 3
  • 26
  • 36