Who makes it into @INC first?

Perl finds modules by looking through the list of directories in @INC. There are many ways to add paths to that array, and although I’ve used all of them at some point, I’ve never quite thought about what happens when I use all of them together.

Remember that Perl uses the first matching module name it finds then
stops looking. It does not have a designed way to determine a best match and newest match or anything fancy. Whatever it finds first is the winner. This also means that someone could add paths to @INC and force your program to run their version of a module, even maliciously. This is something I write about in the “Secure Programming Techniques” chapter of Mastering Perl.

So who gets there first? Start with the default @INC. Running perl -V (capital V) shows a bunch of information and the list of the search paths at the end. These paths are set when you (or whoever) compiled that particular perl binary:

$ perl -V
Summary of my perl5 (revision 5 version 30 subversion 1) configuration:

...

  @INC:
    /usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1/darwin-2level
    /usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1
    /usr/local/perls/perl-5.30.1/lib/5.30.1/darwin-2level
    /usr/local/perls/perl-5.30.1/lib/5.30.1

Here’s a small program to show the paths:

#!perl
use v5.10;

say "The search paths are:\n\t",
	join "\n\t", @INC;

You see the same paths:

$ perl order.pl
The search paths are:
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1
	/usr/local/perls/perl-5.30.1/lib/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/5.30.1

Inside a program, you can add to @INC just as you can with any array. Typically you do this as soon as you can during the compile-phase because use statements do their work at compile time:

BEGIN { unshift @INC, '/some/path'; }

The lib does this for you, so use that:

#!perl
use v5.10;

use lib qw(/from/use/lib);

say "The search paths are:\n\t",
	join "\n\t", @INC;

Run the program again and you see that the new path shows up at the front:

$ perl order.pl
The search paths are:
	/from/use/lib
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1
	/usr/local/perls/perl-5.30.1/lib/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/5.30.1

But there are other ways. The -I switch adds a path too:

$ perl -I/from/dash-i-outside order.pl
The search paths are:
	/from/use/lib
	/from/dash-i-outside
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1
	/usr/local/perls/perl-5.30.1/lib/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/5.30.

But you can put that on the shebang line too:

#!perl -I/from/dash-i-inside
use v5.10;

use lib qw(/from/use/lib);

say "The search paths are:\n\t",
	join "\n\t", @INC;

The inside -I from the shebang line shows up earlier:

$ perl -I/from/dash-i-outside order.pl
The search paths are:
	/from/use/lib
	/from/dash-i-inside
	/from/dash-i-outside
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1
	/usr/local/perls/perl-5.30.1/lib/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/5.30.1

Try it with more than one -I. The first -I from the command line shows up earlier in @INC:

$ perl -I/from/dash-i-outside -I/also/from/dash-i-outside order.pl
The search paths are:
	/from/use/lib
	/from/dash-i-inside
	/from/dash-i-outside
	/also/from/dash-i-outside
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1
	/usr/local/perls/perl-5.30.1/lib/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/5.30.1

The PERL5OPT allows you to set default options, so put a -I in there. That one shows up earlier than the path you explicitly set:

$ export PERL5OPT=-I/from/PERL5OPT
$ perl -I/from/dash-i-outside order.pl
The search paths are:
	/from/use/lib
	/from/dash-i-inside
	/from/PERL5OPT
	/from/dash-i-outside
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1
	/usr/local/perls/perl-5.30.1/lib/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/5.30.1

You can set PERL5LIB with a path, and that shows up right before the default paths:

$ export PERL5LIB=/from/PERL5LIB
$ perl -I/from/dash-i-outside order.pl
The search paths are:
	/from/use/lib
	/from/dash-i-inside
	/from/PERL5OPT
	/from/dash-i-outside
	/from/PERL5LIB
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1
	/usr/local/perls/perl-5.30.1/lib/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/5.30.1

Remember that taint mode ignores the environment, though. The values from PERL5OPT and PERL5LIB disappear:

$ perl -I/from/dash-i-outside -I/also/from/dash-i-outside -T order.pl
The search paths are:
	/from/use/lib
	/from/dash-i-inside
	/from/dash-i-outside
	/also/from/dash-i-outside
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/site_perl/5.30.1
	/usr/local/perls/perl-5.30.1/lib/5.30.1/darwin-2level
	/usr/local/perls/perl-5.30.1/lib/5.30.1

Comments are closed.