Realbin
Part 1: THE PROBLEM WITH PATHS
So you've written a Perl script, and it needs to find its companion modules or config files. You hardcode the path, deploy it, and... nothing works. Sound familiar?The issue is that scripts get symlinked, moved, or called from different directories. Your script has no idea where it actually lives.
Enter FindBin. But not just any FindBin variable - we're talking about $RealBin, the one that tells you the REAL truth.
Part 2: THE MAGIC INCANTATION
Here's the spell you need:That's it. Two lines. Your script now knows exactly where it lives, and it can find modules sitting next to it or in a lib/ subdirectory.use FindBin qw($RealBin); use lib ($RealBin, "$RealBin/lib");
Let's break it down:
This imports $RealBin, which contains the absolute, symlink-resolved path to the directory containing your script. Not where you called it from. Not some symlinked location. The REAL directory.use FindBin qw($RealBin);
This adds both the script's directory and its lib/ subdirectory to @INC, so Perl can find your local modules.use lib ($RealBin, "$RealBin/lib");
Part 3: $Bin vs $RealBin - THE SHOWDOWN
FindBin exports several variables, but the two you'll see most often are $Bin and $RealBin. What's the difference?Let's say you have this setup:$Bin - The directory of the script (may contain symlinks) $RealBin - The REAL directory (all symlinks resolved)
If someone runs /usr/local/bin/worker:/opt/myapp/bin/worker.pl # The actual script /usr/local/bin/worker -> /opt/myapp/bin/worker.pl # A symlink
See the problem? If your modules are in /opt/myapp/lib, using $Bin would look in /usr/local/lib - completely wrong!$Bin = "/usr/local/bin" # Where the symlink lives $RealBin = "/opt/myapp/bin" # Where the script REALLY lives
$RealBin follows the rabbit hole all the way down and gives you the truth.
Part 4: REAL-WORLD PATTERNS
Here's how I structure most of my projects:And in myapp.pl:myapp/ ├── bin/ │ └── myapp.pl ├── lib/ │ └── MyApp/ │ └── Core.pm └── conf/ └── settings.yaml
The beauty here is that it doesn't matter where you symlink the script or how you invoke it. $RealBin always points home.#!/usr/bin/env perl use strict; use warnings; use FindBin qw($RealBin); use lib "$RealBin/../lib"; use MyApp::Core; my $config = "$RealBin/../conf/settings.yaml"; # Now you can load config from the right place!
Part 5: BEST PRACTICES
- ALWAYS use $RealBin over $Bin unless you specifically need symlink paths
- Set up your lib path BEFORE loading your modules:
# GOOD use FindBin qw($RealBin); use lib "$RealBin/lib"; use MyModule; # BAD - MyModule won't be found! use MyModule; use FindBin qw($RealBin); use lib "$RealBin/lib";
- For complex projects, consider a single "bootstrap" approach:
use FindBin qw($RealBin); BEGIN { use lib "$RealBin/../lib"; }
- Combine with File::Spec for maximum portability:
use FindBin qw($RealBin); use File::Spec; my $config = File::Spec->catfile($RealBin, '..', 'conf', 'app.yaml');
Part 6: THE GOTCHA
There's one edge case to know about: FindBin only works reliably at compile time. If you're doing fancy things with eval or require at runtime, the values might not be what you expect.The solution? Capture $RealBin early:
$RealBin is one of those boring-sounding features that solves a genuinely annoying problem. Once you start using it, you'll wonder how you ever deployed scripts without it.use FindBin qw($RealBin); my $APP_ROOT = $RealBin; # Captured immediately # Later, even in evals, $APP_ROOT is solid
Your scripts will finally know where they live. And that's the truth.
perl.gg