perl.gg / hidden-gems

Realbin

2024-08-11

Part 1: THE PROBLEM WITH PATHS

This page explains how to locate your Perl modules and config files reliably using FindBin and $RealBin. When you hardcode a path into a script, deployment breaks the moment someone moves the file or wraps it in a symlink. Scripts run from different directories, but they do not know their actual location on disk. $RealBin solves that problem by giving you the real, symlink-resolved directory.

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. It reports the real location on disk, not the directory you typed into the terminal and not some intermediate symlink.
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, which is completely wrong. $RealBin follows the chain of symlinks all the way down and returns the actual directory.

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 does not matter where you symlink the script or how you invoke it. $RealBin always points to the real source directory, so your paths stay correct regardless of the call chain.

Part 5: BEST PRACTICES

  1. Always use $RealBin over $Bin unless you specifically need the raw symlink path, and you probably do not.
  2. Set up your lib path before you load 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 run eval or require later in the program, the values might not match what you expect. The fix is to 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 will wonder how you ever deployed scripts without it. Your scripts will finally know where they live, and that is the truth.

perl.gg