Local Packages
Ever wanted to organize your Perl code into reusable pieces without installing anything to the system? Local packages are your answer.No CPAN. No root access. No installation headaches. Just drop your modules next to your script and go.
Part 1: THE PROBLEM
You've got a script. It's growing. Functions everywhere. Copy-paste between files. It's a mess.The solution: break it into modules. But installing modules system-wide feels like overkill for a personal project.
Local packages solve this. Your modules live right next to your script. Portable, simple, no fuss.
Part 2: FINDING YOURSELF
First, you need to know where your script lives:$RealBin gives you the directory containing your script. Not where you ran it from - where it actually lives on disk.use FindBin qw($RealBin);
The "Real" versions follow symlinks. If someone symlinked your script, $RealBin points to the actual location, not the symlink's directory.VARIABLE WHAT IT GIVES YOU ---------- ---------------------------------------- $RealBin Directory containing the script $RealScript Name of the script itself $Bin Same as $RealBin (legacy name) $Script Same as $RealScript (legacy name)
Part 3: TELLING PERL WHERE TO LOOK
The use lib pragma adds directories to @INC - Perl's module search path. Now Perl will look in:use lib ($RealBin, "$RealBin/lib");
Order matters. Perl searches left to right, stops at the first match. Your local modules override system ones.1. The script's directory ($RealBin) 2. A lib subdirectory ($RealBin/lib) 3. All the standard system locations
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
Part 4: YOUR FIRST LOCAL MODULE
Create MyUtils.pm in the same directory as your script:That trailing 1; is required. It tells Perl "this module loaded successfully." Without it, your script dies with a cryptic error.package MyUtils; use strict; use warnings; use Exporter qw(import); our @EXPORT_OK = qw(greet square); sub greet { my ($name) = @_; return "Hello, $name!"; } sub square { my ($n) = @_; return $n * $n; } 1;
Part 5: USING YOUR MODULE
Now your main script can use it:Output:#!/usr/bin/env perl use strict; use warnings; use FindBin qw($RealBin); use lib ($RealBin, "$RealBin/lib"); use MyUtils qw(greet square); print greet("World"), "\n"; print "5 squared is ", square(5), "\n";
The functions come in clean, no package prefix needed.Hello, World! 5 squared is 25
Part 6: THE EXPORTER DANCE
Exporter is Perl's mechanism for making functions available to importers. Let's break down what's happening:This imports Exporter's import() method into your package. That method handles the qw(...) list when someone use's your module.use Exporter qw(import);
Functions listed here CAN be imported on request. The caller has to explicitly ask for them.our @EXPORT_OK = qw(greet square);
Compare to:
Functions in @EXPORT are imported automatically. No request needed.our @EXPORT = qw(greet);
The Perl community prefers @EXPORT_OK. Let the caller decide what they want in their namespace.ARRAY BEHAVIOR ----------- ---------------------------------------- @EXPORT Auto-imported (considered impolite) @EXPORT_OK Imported on request (the polite way) %EXPORT_TAGS Named groups of exports
Part 7: ORGANIZING INTO SUBDIRECTORIES
As your project grows, you'll want a lib directory:Create lib/MathOps.pm:my_project/ main.pl MyUtils.pm lib/ MathOps.pm StringOps.pm
Your script can use both:package MathOps; use strict; use warnings; use Exporter qw(import); our @EXPORT_OK = qw(add subtract multiply divide); sub add { $_[0] + $_[1] } sub subtract { $_[0] - $_[1] } sub multiply { $_[0] * $_[1] } sub divide { my ($a, $b) = @_; die "Division by zero" if $b == 0; return $a / $b; } 1;
use MyUtils qw(greet); use MathOps qw(add multiply);
Part 8: THE COMPLETE EXAMPLE
Here's a full working setup. Three files:main.pl:
MyUtils.pm (same directory as main.pl):#!/usr/bin/env perl use strict; use warnings; use FindBin qw($RealBin); use lib ($RealBin, "$RealBin/lib"); use MyUtils qw(greet square); use MathOps qw(add multiply); # Use the imported functions print greet("Perl Hacker"), "\n"; print "3 squared: ", square(3), "\n"; print "5 + 7 = ", add(5, 7), "\n"; print "4 * 8 = ", multiply(4, 8), "\n";
lib/MathOps.pm:package MyUtils; use strict; use warnings; use Exporter qw(import); our @EXPORT_OK = qw(greet square); sub greet { my ($name) = @_; return "Hello, $name!"; } sub square { my ($n) = @_; return $n * $n; } 1;
package MathOps; use strict; use warnings; use Exporter qw(import); our @EXPORT_OK = qw(add subtract multiply divide); sub add { $_[0] + $_[1] } sub subtract { $_[0] - $_[1] } sub multiply { $_[0] * $_[1] } sub divide { my ($a, $b) = @_; die "Division by zero" if $b == 0; return $a / $b; } 1;
Part 9: IMPORTING EVERYTHING
Sometimes you want all exports. Use the :all tag:Now callers can do:package MyUtils; use Exporter qw(import); our @EXPORT_OK = qw(greet square cube); our %EXPORT_TAGS = ( all => \@EXPORT_OK );
And they get greet, square, and cube without listing each one.use MyUtils qw(:all);
Part 10: NESTED PACKAGES
You can have hierarchical module names:lib/My/Utils.pm:lib/ My/ Utils.pm Math/ Advanced.pm
lib/My/Math/Advanced.pm:package My::Utils; # ... 1;
Use them with:package My::Math::Advanced; # ... 1;
The :: in the package name maps to / in the filesystem.use My::Utils qw(some_function); use My::Math::Advanced qw(factorial);
Part 11: COMMON MISTAKES
Forgot the 1; at the end:Fix: Add 1; as the last line of your module.MyUtils.pm did not return a true value at main.pl line 5.
Wrong directory in use lib:
Fix: Check that $RealBin points where you think. Add:Can't locate MyUtils.pm in @INC...
Package name doesn't match filename:print "Looking in: $RealBin\n";
Fix: The package statement must match the .pm filename exactly.Undefined subroutine &MyUtilz::greet called...
Part 12: WHY NOT JUST require?
You can skip Exporter entirely with require and explicit calls:But this has drawbacks:require "$RealBin/MyUtils.pm"; print MyUtils::greet("World");
For quick scripts, require works. For maintainable code, use Exporter.* No import list - you always use full package names * Runs at runtime, not compile time - errors caught later * Less idiomatic - other Perl programmers expect use
perl.gg.----------------. / \ | my_project/ | | +-----------+ | | | main.pl | | | +-----------+ | | | MyUtils.pm| | | +-----------+ | | | lib/ | | | | MathOps | | | +-----------+ | \ / '----------------'