Perl Circus - Three Rings of Perl Tricks.

More Modules

List the location of some module

If a module has been installed via CPAN for the perl in your current path, you can quickly find its source code with the following.

$ perldoc -l Some::Module

List the location of every module

The following will list the name, location, version number and install date, for every module installed with the make install command for the perl in your current path.

$ perldoc perllocal

For a more comprehensive list, but with less details, the following will print every file in your @INC paths that ends with ".pm".

$ perl -MFile::Find=find -MFile::Spec::Functions -Tlwe \
> 'find {wanted=>sub{ print canonpath $_ if /\.pm$/ }, no_chdir=>1}, @INC'

Or use Tom Christiansen's pmtools, now maintained by Mark Leighton Fisher.

Show all modules that need updates

The CPAN installer can compare the $VERSION value of all installed modules with the latest available. The simple r command will print a complete report.

$ sudo perl -MCPAN -eshell
cpan> r

Install a module "by hand"

Most Perl module authors distribute their code so that it can be installed in the "usual way." This requires that a compressed tar file be downloaded, opened, then installed using make. This process will typically place files in a system-wide library which can be accessed by every user, and so will require superuser priviledges (hence the use of sudo).

$ wget http://search.cpan.org/CPAN/authors/id/T/TI/TIMA/Text-Greeking-0.11.tar.gz
$ tar -xvzf Text-Greeking-0.11.tar.gz
$ cd Text-Greeking-0.11
$ perl Makefile.PL
$ make
$ make test
$ sudo make install

This process can be more automated by the use of the CPAN installer.

$ sudo perl -MCPAN -eshell
cpan> install Text::Greeking
cpan> q

Install a module in a local library

If you don't have permissions to install a module in a system-wide library, or just want to keep a new module out of the general library, you can configure the makefile so that it will prefix the installation directory with a local library path of your choosing.

$ wget http://search.cpan.org/CPAN/authors/id/T/TI/TIMA/Text-Greeking-0.11.tar.gz
$ tar -xvzf Text-Greeking-0.11.tar.gz
$ cd Text-Greeking-0.11
$ perl Makefile.PL LIB=/home/michael/lib
$ make
$ make test
$ make pure_install

Note that I used the pure_install target instead of install, in order to avoid adding this module to perllocal.pod.

To use such locally installed modules, you'll need to add that library location to perl's @INC paths. This can be done in several ways, but the simplest is to use the lib module near the top of your script.

use lib '/home/michael/lib';
use Text::Greeking;

print Text::Greeking->new()->generate;

The equivalent program can be entered on the command line like so...

$ perl -I/home/michael/lib -MText::Greeking \
> -e'print Text::Greeking->new()->generate;'

If you prefer not to add a lot of use lib statements to your code you can set an envirnoment variable that will have the same effect (as long as you are logged in to that environment). In bash the command would be...

$ EXPORT PERLLIB=$PERLLIB:/home/michael/lib

Install a module in a local library using the CPAN installer

The CPAN installer will automate the same steps you would do yourself when installing a CPAN module, following a configuration set-up it reads from a file called <perl root>/CPAN/Config.pm. This will put new modules in a system-wide location, but you can create your own configuration file to install modules in a local location.

The simplest way to accomplish this is to copy the system-wide configuration into your home directory and then modify it. For example...

$ mkdir -p ~/.cpan/CPAN
$ cp /usr/lib/perl5/5.8.8/CPAN/Config.pm ~/.cpan/CPAN/MyConfig.pm
$ perl -p -i -e 's!/root/!/home/michael/!g' ~/.cpan/CPAN/MyConfig.pm

That last line may need to be adjusted depending on how CPAN was initially configured. You can do this by hand with a text editor, checking the MyConfig.pm file to make sure it all looks sane when you are finished. While you have it open you'll also need to add your local module library location; specifically, change (or add) the makepl_arg configuration line to point to the desired directory, as shown below...

'makepl_arg' => q[LIB=/home/michael/lib]

Now you can run the CPAN installer under your own user id, and it will place it's modules in your library.

Install and use multiple versions of a module

You may want to install a newer version of a module but don't want to break existing scripts that rely on an already installed older version. You can keep the versions sorted by using another module, named only. This gives you very fine control over precisely which version, or range of versions, of a module you want to use. First, make sure you have the only::install module installed.

During the installation of only::install you can choose a directory to store separately versioned modules in. For convenience enter a directory under one of the locations already in your @INC path. Then to install a separate version of any module you simply change the last line of the "usual" module install procedure to include a reference to the only module. For example...

$ wget http://search.cpan.org/CPAN/authors/id/R/RP/RPRICE/Acme-Terror-UK-0.03.tar.gz
$ tar -xvzf Acme-Terror-UK-0.03.tar.gz
$ cd Acme-Terror-UK-0.03
$ perl Makefile.PL
$ make
$ make test
$ sudo perl -Monly=install

You can install as many different versions of the same module as you like using the method shown above; if you already have a version in one of the default locations, it won't be affected. Then, when you want to use a particular version, use the syntax shown below...

use only Acme::Terror::UK => '0.03';

my $terror = Acme::Terror::UK->new();
print $terror->fetch;

Use a module conditionally

If a module is installed in one of the locations listed in perl's special @INC array, then using that module is as simple as writing...

use Some::Module;

It is important to note that use is a compile-time function, so it is evaluated before any runtime code. For that reason you cannot do the following...

my $x = 0;
if ($x) { use Some::Module } # won't work!

The module will always be used in the above example regardless of what $x is because the if () construct is evaluated at runtime, well after the compile time use has already happened. You can force use to happen at the same time as the surrounding code by putting it inside an eval string...

my $x = 0;
if ($x) { eval 'use Some::Module'; die if $@ } # runtime

If you prefer the module used at compile time, you can wrap the above example in a BEGIN { } block, which is always evaluated at compile time, but this comes with caveats: because BEGIN blocks are evaluated before the runtime portion of your program, only variables that are in existence at compile time can be used in the condition test. You can use special variables like %ENV and @ARGV, but not runtime variables defined outside the BEGIN block.

BEGIN {
    my $x = 0;
    if ($x) { eval 'use Some::Module'; die if $@ } # compile time
}

Another way to do it is with use if (however all the same compile time restrictions on what variables can be used in the condition test still apply)...

my $x = 0;
use if $x, Some::Module => ();                      # won't work
use if ($ENV{HOME} =~ /jimbo/), Some::Module => (); # ok

Unlike use, the require filename construct is evaluated at runtime, but it doesn't automatically import the module functions that are tagged for export, so you could try the following...

my $x = 0;
if ($x) {
    require Some::Module;
    import Some::Module qw();
}

Again, you could wrap the above in a BEGIN { } block to have it happen at compile time. SEE ALSO Jos Boumans' Module-Load-Conditional.

Use a library directory conditionally

When you use a module perl looks in each of the library directories in the special @INC array to try to find that module file. You can conditionally change the @INC array, and thus change which version of a module gets used, but in order for that to have any effect you must do so at compile time, and before any modules are used. The following demonstrates one way to do that...

BEGIN {
    if ($ENV{HOME} =~ /michael/) {
        eval 'use lib ("$ENV{HOME}/mylib/")';
    }
}
use Some::Module; # the one in mylib, if I'm michael

Remember that the BEGIN block is evaluated at compile time, and therefore you can only use variables that exist at compile time (such as the special %ENV variable) in your condition test.

Find out which module is getting used

$ perl -e 'print shift @{[grep{$_.="/Test/More.pm", -e} @INC]};'

Knowing that when you say "use Test::More" (or use any::module) in your perl script, perl will load the code in a file under one of the library locations found in the special "INC" array; you can find exactly which module by searching that array yourself. This one-liner will loop over each of the directories in @INC trying to find the module you specify in the grep block. Note that while the "use" pragma takes a module name like "Test::More," the actual code will be found in a file named "Test/More.pm".

Uninstall a module

Surprisingly, the CPAN installer doesn't provide any way to uninstall modules. However, the standard perl distribution comes with a couple of ExtUtils modules that can track down every file placed on your system when a module was installed, including documentation files, and packing lists. The script below uses those modules and deletes all the files and directories (if empty) they find. Naturally, you'll need to run this with sufficient permissions to remove such files and, as when deleting anything, use caution.

use IO::Dir;
use ExtUtils::Packlist;
use ExtUtils::Installed;

my $install = ExtUtils::Installed->new();
my $module = $ARGV[0] or die "Usage: $0 Module::Name\n"; 

foreach my $file ($install->files($module)) {
    ask_delete($file);
}
ask_delete($install->packlist($module)->packlist_file());

sub ask_delete {
    my $file = shift;
    
    print qq(\n> Really delete file "$file"? [y/n]\n);
    chop (my $confirm = <STDIN>);
    return unless ($confirm =~ /^y/i);
    
    (unlink $file)? print qq(Deleted module file "$file".\n)
        : warn qq(Couldn't delete "$file". $!\n);

    foreach my $dir ($install->directory_tree($module)) {
        next unless(is_empty($dir));
        
        (rmdir($dir))? print qq(Deleted empty dir "$dir"\n)
            : warn qq(Couldn't delete "$dir" $!\n);
    }
}

sub is_empty {
    my $dh = IO::Dir->new(+shift) or return;
    my $count = scalar(grep{!/^\.\.?$/} $dh->read());
    $dh->close();
    return($count==0);
}

	

You would use this script in the following way (assume you named it "uninstall")...

$ sudo perl uninstall Some::Module