Perl beginner

How to Check If a Perl Module Is Installed (and Loadable)

Question

how to check if a perl module is installed

How to Check If a Perl Module Is Installed (and Loadable)

Difficulty: Beginner (with some Intermediate best practices)

Tags: perl, cpan, modules, dependency-management, @INC, PERL5LIB, local::lib, require, use

In Perl, “is a module installed?” is really two closely related questions:

  1. Is the module’s file present somewhere on disk? (e.g., Some/Module.pm exists)
  2. Can this particular Perl interpreter load it right now? (i.e., will require/use succeed with the current @INC, environment variables, and permissions?)

For production and automation, you almost always care about #2: whether the module is loadable by the same Perl binary and environment your app uses. A module might exist on disk but not be loadable if you’re running a different Perl, a different @INC, or missing compiled dependencies.

Key Concept: @INC (where Perl looks for modules)

When you do use Foo::Bar; or require Foo::Bar;, Perl searches for Foo/Bar.pm through the list of directories in @INC. That list is influenced by:

  • The Perl installation (system Perl vs perlbrew/plenv/asdf, etc.)
  • PERL5LIB (adds library paths)
  • use lib statements in your code
  • local::lib (common for per-user installs)
  • Container/runtime filesystem layout

This is why two commands can disagree:

  • Your shell might run /usr/bin/perl
  • Your app (or cron job) might run /opt/perl/bin/perl

Always check with the same Perl binary your program uses.

Fast Command-Line Checks (common in ops and debugging)

These are not “Perl code examples” (the runnable examples come later), but they’re very practical:

  • Try to load it:
    perl -MSome::Module -e1

    If the module is loadable, it exits successfully (exit code 0) and prints nothing. If it’s not loadable, you’ll see an error like “Can’t locate Some/Module.pm in @INC …”.

  • Find the module file Perl would use:
    perldoc -l Some::Module

    If installed, prints the path to the module’s file. If not installed, you’ll get a “No documentation found” message or a non-zero exit status (depends on platform and configuration).

  • Ask CPAN clients (if present):
    cpan -D Some::Module
    cpanm --info Some::Module

    Useful, but remember: CPAN tooling can be configured per-user and may not reflect what the runtime interpreter sees unless you’re careful about which Perl and environment it uses.

Best Practice Approach in Perl Code

Inside Perl code, the safest, most common pattern is:

  • Convert a module name to a file path (Foo::BarFoo/Bar.pm)
  • Attempt to require it inside an eval block
  • Optionally call ->import if you need exported symbols (to mimic use)
  • Inspect $@ if loading fails

This is preferred over eval "use $module" when the module name can vary, because string eval has code-injection risks if the input is not trusted.

Runnable Perl Code Examples (with Expected Output)

Example 1: Basic “installed/loadable?” check using require

This example checks one core module (File::Spec, which should be present in any normal Perl distribution) and one fake module that is guaranteed to be missing.

#!/usr/bin/env perl
use strict;
use warnings;

sub module_to_file {
  my ($module) = @_;
  (my $file = $module) =~ s{::}{/}g;
  return "$file.pm";
}

sub is_module_loadable {
  my ($module) = @_;
  my $file = module_to_file($module);
  return eval { require $file; 1 } ? 1 : 0;
}

for my $module ("File::Spec", "Some::Made::Up") {
  print "$module: ", (is_module_loadable($module) ? "installed" : "NOT installed"), "\n";
}

Expected output:

File::Spec: installed
Some::Made::Up: NOT installed

Why this works: require searches @INC for the module file. Wrapping it in eval prevents the program from dying; instead, failures populate $@.

Example 2: Optional dependency with a safe fallback

Real applications often have optional features (faster JSON, optional DB drivers, optional telemetry). The best pattern is: try the preferred module; if it fails, fall back to a known-good alternative.

#!/usr/bin/env perl
use strict;
use warnings;

sub module_to_file {
  my ($module) = @_;
  (my $file = $module) =~ s{::}{/}g;
  return "$file.pm";
}

sub load_module {
  my ($module) = @_;
  my $file = module_to_file($module);
  return eval { require $file; 1 } ? 1 : 0;
}

my $preferred = "Some::Made::Up";  # pretend this is a faster backend
my $fallback  = "JSON::PP";        # core on many perls

my $json_class;
if (load_module($preferred)) {
  $json_class = $preferred;
} else {
  die "Neither $preferred nor $fallback could be loaded\n" unless load_module($fallback);
  $json_class = $fallback;
}

my $json = $json_class->new->canonical(1);
my $encoded = $json->encode({ hello => "world" });

print "Using JSON engine: $json_class\n";
print "JSON: $encoded\n";

Expected output:

Using JSON engine: JSON::PP
JSON: {"hello":"world"}

Notes: This pattern keeps your app resilient: it runs with the fallback, but you can still deploy the preferred module in environments where you want better performance.

Example 3: Report why a module is missing (clean, stable error messaging)

When you’re building diagnostics (health checks, startup self-tests, CLI doctor commands), it’s useful to show why loading failed without dumping environment-specific @INC noise.

#!/usr/bin/env perl
use strict;
use warnings;

sub module_to_file {
  my ($module) = @_;
  (my $file = $module) =~ s{::}{/}g;
  return "$file.pm";
}

sub try_load {
  my ($module) = @_;
  my $file = module_to_file($module);

  my $ok = eval { require $file; 1 };
  return (1, "") if $ok;

  my ($first_line) = split /\n/, ($@ // "");
  $first_line =~ s/ at .*//;  # remove file/line suffix for stable output
  return (0, $first_line || "Unknown error");
}

for my $module ("strict", "Some::Made::Up") {
  my ($ok, $err) = try_load($module);
  if ($ok) {
    print "$module: ok\n";
  } else {
    print "$module: missing ($err)\n";
  }
}

Expected output:

strict: ok
Some::Made::Up: missing (Can't locate Some/Made/Up.pm in @INC (you may need to install the Some::Made::Up module))

Checking a Module Version (when you need a minimum)

Sometimes “installed” isn’t enough; you need “installed at least version X”. Perl supports this directly:

  • use Some::Module 1.23; (compile-time)
  • Some::Module->VERSION(1.23); (runtime; dies if too old)

Important behavior:

  • use happens at compile time; if it fails, your program won’t start (good for hard requirements).
  • require happens at runtime; you can decide what to do if it fails (good for optional features).
  • Some modules don’t define $VERSION clearly; version checks can be awkward for those (rare for CPAN modules, more common for tiny internal packages).

Best Practices (Production-Ready Guidance)

  • Check using the same Perl binary your app uses. On systems with multiple Perls, perl -v and which perl matter.
  • Fail fast for required dependencies. Use plain use Module; for required modules so startup fails loudly and early.
  • Use runtime loading only for optional dependencies. Prefer eval { require ... } and then feature-gate functionality.
  • Avoid string eval for dynamic module names. If the module name comes from user input or config, string eval can be a code-injection vector. Convert to a filename and require that.
  • Remember that “installed” can differ by environment. Cron, systemd services, and web servers may not inherit your shell’s PERL5LIB or local::lib setup.
  • Prefer declaring dependencies instead of probing for them. For CPAN distributions, use cpanfile, Makefile.PL, or Build.PL so installation tools handle it up front.

Common Pitfalls

  • Using the wrong Perl. “It works in my terminal” but fails in production often means different interpreters or different @INC.
  • Confusing “file exists” with “module loads.” A module might exist but fail to load due to missing shared libraries (XS modules), missing transitive dependencies, or syntax incompatible with your Perl version.
  • Forgetting that use also imports symbols. require Module; loads code but does not automatically call Module->import. If you relied on exported functions, you must call Module->import(...) yourself.
  • Reading too much into %INC. %INC records what’s been loaded in the current process. It doesn’t tell you what’s installed system-wide; it tells you what has already been required.
  • Assuming perldoc always works. Minimal containers sometimes omit perl-doc packages, so perldoc -l may not be available even if the module is.

Real-World Usage Patterns

  • Startup dependency check: At process start, load required modules normally; for optional ones, test and log capability flags (e.g., “TLS enabled”, “fast JSON enabled”).
  • Plugin discovery: Read a list of plugin module names from config, require each, and keep only those that load successfully.
  • Health checks and diagnostics: Build a “doctor” command that attempts to load critical modules and prints actionable errors (missing module, wrong version, missing shared library).
  • Deployment verification: In CI/CD or container builds, run a script that loads every required module with perl -M... to ensure the image truly contains everything.

Summary

The most reliable way to check if a Perl module is installed is to try to load it with the same Perl interpreter and environment your application uses. In scripts, prefer eval { require ... } for optional modules and plain use for required dependencies. When you need minimum versions, use Perl’s built-in version checking. Always account for @INC and multi-Perl environments to avoid confusing false negatives.

Verified Code

Executed in a sandbox to capture real output. • 14ms

Tip: edit code and use “Run (Browser)”. Server runs always execute the published, verified snippet.
STDOUT
File::Spec: installed
Some::Made::Up: NOT installed
STDERR
(empty)

Was this helpful?

Related Questions