How to Add a Directory to The Perl Library Path at The System Level

How can my Perl script find its module in the same directory?

The simplest approach I found it to use FindBin module. Like this:

use FindBin;
use lib $FindBin::Bin;

Generally I prefer to have my scripts provided in such a way that programs are in whatever/bin, and libraries are in whatever/lib

In these situations I use a slightly more complicated approach:

use Cwd qw(abs_path);
use FindBin;
use lib abs_path("$FindBin::Bin/../lib");

The abs_path call is to make the @INC contain whatever/lib, and not whatever/bin/../lib - it's just a slight change, but makes reading error messages easier.

How is Perl's @INC constructed? (aka What are all the ways of affecting where Perl modules are searched for?)

We will look at how the contents of this array are constructed and can be manipulated to affect where the Perl interpreter will find the module files.

  1. Default @INC

    Perl interpreter is compiled with a specific @INC default value. To find out this value, run env -i perl -V command (env -i ignores the PERL5LIB environmental variable - see #2) and in the output you will see something like this:

    $ env -i perl -V
    ...
    @INC:
    /usr/lib/perl5/site_perl/5.18.0/x86_64-linux-thread-multi-ld
    /usr/lib/perl5/site_perl/5.18.0
    /usr/lib/perl5/5.18.0/x86_64-linux-thread-multi-ld
    /usr/lib/perl5/5.18.0
    .

Note . at the end; this is the current directory (which is not necessarily the same as the script's directory). It is missing in Perl 5.26+, and when Perl runs with -T (taint checks enabled).

To change the default path when configuring Perl binary compilation, set the configuration option otherlibdirs:

Configure -Dotherlibdirs=/usr/lib/perl5/site_perl/5.16.3


  1. Environmental variable PERL5LIB (or PERLLIB)

    Perl pre-pends @INC with a list of directories (colon-separated) contained in PERL5LIB (if it is not defined, PERLLIB is used) environment variable of your shell. To see the contents of @INC after PERL5LIB and PERLLIB environment variables have taken effect, run perl -V.

    $ perl -V
    ...
    %ENV:
    PERL5LIB="/home/myuser/test"
    @INC:
    /home/myuser/test
    /usr/lib/perl5/site_perl/5.18.0/x86_64-linux-thread-multi-ld
    /usr/lib/perl5/site_perl/5.18.0
    /usr/lib/perl5/5.18.0/x86_64-linux-thread-multi-ld
    /usr/lib/perl5/5.18.0
    .
  2. -I command-line option

    Perl pre-pends @INC with a list of directories (colon-separated) passed as value of the -I command-line option. This can be done in three ways, as usual with Perl options:

    • Pass it on command line:

      perl -I /my/moduledir your_script.pl
    • Pass it via the first line (shebang) of your Perl script:

      #!/usr/local/bin/perl -w -I /my/moduledir
    • Pass it as part of PERL5OPT (or PERLOPT) environment variable (see chapter 19.02 in Programming Perl)

  3. Pass it via the lib pragma

    Perl pre-pends @INC with a list of directories passed in to it via use lib.

    In a program:

    use lib ("/dir1", "/dir2");

    On the command line:

    perl -Mlib=/dir1,/dir2

    You can also remove the directories from @INC via no lib.

  4. You can directly manipulate @INC as a regular Perl array.

    Note: Since @INC is used during the compilation phase, this must be done inside of a BEGIN {} block, which precedes the use MyModule statement.

    • Add directories to the beginning via unshift @INC, $dir.

    • Add directories to the end via push @INC, $dir.

    • Do anything else you can do with a Perl array.

Note: The directories are unshifted onto @INC in the order listed in this answer, e.g. default @INC is last in the list, preceded by PERL5LIB, preceded by -I, preceded by use lib and direct @INC manipulation, the latter two mixed in whichever order they are in Perl code.

References:

  • perldoc perlmod
  • perldoc lib
  • Perl Module Mechanics - a great guide containing practical HOW-TOs
  • How do I 'use' a Perl module in a directory not in @INC?
  • Programming Perl - chapter 31 part 13, ch 7.2.41
  • How does a Perl program know where to find the file containing Perl module it uses?

There does not seem to be a comprehensive @INC FAQ-type post on Stack Overflow, so this question is intended as one.

When to use each approach?

  • If the modules in a directory need to be used by many/all scripts on your site, especially run by multiple users, that directory should be included in the default @INC compiled into the Perl binary.

  • If the modules in the directory will be used exclusively by a specific user for all the scripts that user runs (or if recompiling Perl is not an option to change default @INC in previous use case), set the users' PERL5LIB, usually during user login.

    Note: Please be aware of the usual Unix environment variable pitfalls - e.g. in certain cases running the scripts as a particular user does not guarantee running them with that user's environment set up, e.g. via su.

  • If the modules in the directory need to be used only in specific circumstances (e.g. when the script(s) is executed in development/debug mode, you can either set PERL5LIB manually, or pass the -I option to perl.

  • If the modules need to be used only for specific scripts, by all users using them, use use lib/no lib pragmas in the program itself. It also should be used when the directory to be searched needs to be dynamically determined during runtime - e.g. from the script's command line parameters or script's path (see the FindBin module for very nice use case).

  • If the directories in @INC need to be manipulated according to some complicated logic, either impossible to too unwieldy to implement by combination of use lib/no lib pragmas, then use direct @INC manipulation inside BEGIN {} block or inside a special purpose library designated for @INC manipulation, which must be used by your script(s) before any other modules are used.

    An example of this is automatically switching between libraries in prod/uat/dev directories, with waterfall library pickup in prod if it's missing from dev and/or UAT (the last condition makes the standard "use lib + FindBin" solution fairly complicated.
    A detailed illustration of this scenario is in How do I use beta Perl modules from beta Perl scripts?.

  • An additional use case for directly manipulating @INC is to be able to add subroutine references or object references (yes, Virginia, @INC can contain custom Perl code and not just directory names, as explained in When is a subroutine reference in @INC called?).

How to go one up folder level using perl?

For working with paths, I'd strongly recommend Path::Tiny.

use Path::Tiny 'path';

my $path = path "/home/tai/Documents/";

for my $child ( $path->children ) {
print "$child\n" if $child->is_file;
}

my $parent = $path->parent;
print "PARENT: $parent\n";

How can I use lib the appropriate directory depending on installation location?

FindBin::libs is excellent for that. I've used it for a while in a large system with no problems at all.

The default invocation looks like it'll work for you, simply:

use FindBin::libs;

This will search for all the ./lib dirs in all the parent directories of the current file's dir and use lib them. So, for example, if your script lives in /home/w/myapp_live/scripts/defurblise_widgets.pl (and use()es FindBin::libs), it will look for:

/home/w/myapp_live/scripts/lib
/home/w/myapp_live/lib
/home/w/lib
/home/lib
/lib # (presumably!)

Any that it finds with be added to you @INC with use lib.

But, if that's not quite what you need, it's a very flexible module. I'd be surprised if you can't find a way to make it do what you want.

Perl: Using lib from external dir, overriding locally installed one

If you want to just modify your environment to always use the new location without futzing with your scripts, the PERLLIB environment variable is your friend.

A script to dump the include list:

#!/usr/bin/perl -w
use Data::Dumper;
print Dumper(@INC);

Here's my @INC initially:

$VAR1 = '/usr/local/lib64/perl5';
$VAR2 = '/usr/local/share/perl5';
$VAR3 = '/usr/lib64/perl5/vendor_perl';
$VAR4 = '/usr/share/perl5/vendor_perl';
$VAR5 = '/usr/lib64/perl5';
$VAR6 = '/usr/share/perl5';
$VAR7 = '.';

Now I'll modify it and run the program again:

export PERLLIB="/home/mhkohne/lib"

$VAR1 = '/home/mhkohne/lib';
$VAR2 = '/usr/local/lib64/perl5';
$VAR3 = '/usr/local/share/perl5';
$VAR4 = '/usr/lib64/perl5/vendor_perl';
$VAR5 = '/usr/share/perl5/vendor_perl';
$VAR6 = '/usr/lib64/perl5';
$VAR7 = '/usr/share/perl5';
$VAR8 = '.';

I'm on a Linux system, and I run the bash shell, you'll need to modify that environment set appropriately for your system.

Using perl system() query with spaces in path variable

OK you have several possibilities for shell expansion with the way you are doing this.

Firstly is using system() with a string. This will break all your paths on the space characters. you can solve this by using system as a list

system('ssh', '-i', 'id_pub', $ssh_addr, 'ls', $remote_dir)

Now we still have a problem as ssh will run the remote code on the remote server in a shell with shell expansion which will break the path on spaces again

So you need to put $remote_dir inside ' characters to stop the remote shell from breaking up the path: giving

system('ssh', '-i', 'id_pub', $ssh_addr, 'ls', "'$remote_dir'")

Hope this helps/works

Note that as the commenters below have said this makes the assumption that $remote_dir has no ' characters in it. You need to be either escaping or parsing $remote_dir to ensure that you don't get a path that looks like /file.txt'; rm -rf / # which will attempt to remove every file on the remote system

How can I find out where a Perl module is installed?

Note: This solution proposes use of a (self-authored) utility that you must download. While it offers what I believe to be helpful features, installing a third-party solution first is not an option for everyone.


I've created whichpm, a cross-platform CLI (Linux, macOS, Window) that locates installed Perl modules by module (package) name, and optionally reports information about them, including detection of accidental duplicates.

Examples

# Locate the Data::Dumper module.
$ whichpm Data::Dumper
/usr/lib/perl/5.18/Data/Dumper.pm

# Locate the Data::Dumper module, and also print
# version information and core-module status.
$ whichpm -v Data::Dumper
Data::Dumper 2.145 core>=5.005 /usr/lib/perl/5.18/Data/Dumper.pm

# Locate the Data::Dumper module and open it in your system's default text
# editor.
$ whichpm -e Data::Dumper

# Look for accidental duplicates of the Foo::Bar module.
# Normally, only 1 path should be returned.
$ whichpm -a Foo::Bar
/usr/lib/perl/5.18/Foo/Bar.pm
./Foo/Bar.pm

# Print the paths of all installed modules.
$ whichpm -a

Installation

Prerequisites: Linux, macOS, or Windows, with Perl v5.4.50 or higher installed.

Installation from the npm registry

With Node.js or io.js installed, install the package as follows:

[sudo] npm install whichpm -g

Manual installation (macOS and Linux)

  • Download the CLI as whichpm.
  • Make it executable with chmod +x whichpm.
  • Move it or symlink it to a folder in your $PATH, such as /usr/local/bin (OSX) or /usr/bin (Linux).


Related Topics



Leave a reply



Submit