perl.gg / hidden-gems

Realbin

2024-08-11

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:
use FindBin qw($RealBin); use lib ($RealBin, "$RealBin/lib");
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.

Let's break it down:

use FindBin qw($RealBin);
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 lib ($RealBin, "$RealBin/lib");
This adds both the script's directory and its lib/ subdirectory to @INC, so Perl can find your local modules.

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?
$Bin - The directory of the script (may contain symlinks) $RealBin - The REAL directory (all symlinks resolved)
Let's say you have this setup:
/opt/myapp/bin/worker.pl # The actual script /usr/local/bin/worker -> /opt/myapp/bin/worker.pl # A symlink
If someone runs /usr/local/bin/worker:
$Bin = "/usr/local/bin" # Where the symlink lives $RealBin = "/opt/myapp/bin" # Where the script REALLY lives
See the problem? If your modules are in /opt/myapp/lib, using $Bin would look in /usr/local/lib - completely wrong!

$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:
myapp/ ├── bin/ │ └── myapp.pl ├── lib/ │ └── MyApp/ │ └── Core.pm └── conf/ └── settings.yaml
And in myapp.pl:
#!/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!
The beauty here is that it doesn't matter where you symlink the script or how you invoke it. $RealBin always points home.

Part 5: BEST PRACTICES

  1. ALWAYS use $RealBin over $Bin unless you specifically need symlink paths
(and you probably don't).
  1. 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";
  1. For complex projects, consider a single "bootstrap" approach:
use FindBin qw($RealBin); BEGIN { use lib "$RealBin/../lib"; }
  1. 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:

use FindBin qw($RealBin); my $APP_ROOT = $RealBin; # Captured immediately # Later, even in evals, $APP_ROOT is solid
$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.

Your scripts will finally know where they live. And that's the truth.

perl.gg