Perl Language
Introduction
Perl is a pragmatic, text-friendly language.
- Scripting language
- Strong text processing
- Dynamic typing
- Multi-paradigm (procedural, OO, functional style)
#!/usr/bin/env perl
use strict;
use warnings;
print "Hello World\n";
VS Code and Perl
Package Installation
sudo apt update
# Core build + Perl tooling
sudo apt install -y build-essential git perl perl-dbg cpanminus
# Common native libs used by CPAN modules
sudo apt install -y libssl-dev libreadline-dev zlib1g-dev libffi-dev libyaml-dev
# Postgres client/dev + DBI drivers
sudo apt install -y libpq-dev libdbi-perl libdbd-pg-perl
# Web framework (optional)
sudo apt install -y libmojolicious-perl
# If you build XS modules that need libperl/libaio
sudo apt install -y libperl-dev libaio-dev
Perl local::lib Environment Variables
Think of `local::lib` like Python `venv`: it gives you a user-scoped install location so your modules and scripts do not touch the system Perl. Unlike `venv`, it does not create a separate Perl executable; it adjusts environment variables so Perl loads modules from your local tree first. These are typical environment variables produced by `local::lib` to install CPAN modules into a user directory instead of system-wide.
PATH="/home/iwiseman/perl5/bin${PATH:+:${PATH}}"; export PATH;
PERL5LIB="/home/iwiseman/perl5/lib/perl5${PERL5LIB:+:${PERL5LIB}}"; export PERL5LIB;
PERL_LOCAL_LIB_ROOT="/home/iwiseman/perl5${PERL_LOCAL_LIB_ROOT:+:${PERL_LOCAL_LIB_ROOT}}"; export PERL_LOCAL_LIB_ROOT;
PERL_MB_OPT="--install_base \"/home/iwiseman/perl5\""; export PERL_MB_OPT;
PERL_MM_OPT="INSTALL_BASE=/home/iwiseman/perl5"; export PERL_MM_OPT;
- PATH: prepends the local Perl `bin` so user-installed scripts are found first.
- PERL5LIB: adds the local Perl library path so `use` and `require` can find modules.
- PERL_LOCAL_LIB_ROOT: tracks the base directory used by `local::lib` (can be stacked).
- PERL_MB_OPT: tells `Module::Build` to install into the local base.
- PERL_MM_OPT: tells `ExtUtils::MakeMaker` to install into the local base.
Running
If you have Perl installed, you can run a script directly.
perl script.pl
Debugging
This ate a day, mainly because there are two issues.
The Gerald Richter Perl language server works but half works and is useless. Remove it. Instead install the Perl Navigator extension.
Changing the keyboard extensions using .vscode/keyboardbindings.json does not work. You to change them under cd %APPDATA%\Code\User\keyboardbindings.json. So this was mine.
[
{
"key": "alt+q",
"command": "workbench.action.tasks.runTask",
"args": "Debug Mojolicious",
"when": "terminalFocus || editorTextFocus"
}
]
Now in your .vscode/tasks.json you can run the debugger. Here it is for my project
{
"version": "2.0.0",
"tasks": [
{
"label": "Debug Mojolicious",
"type": "shell",
"command": "MOJO_REACTOR=POLL perl -d script/app.pl daemon -l http://0.0.0.0:5001",
"problemMatcher": []
}
]
}
Then you need to know the debbuger commands
Perl Debugger Quick Reference
This page provides a concise reference for using the Perl debugger (perl -d), including breakpoints, stepping, inspecting variables, and quitting.
Starting the Debugger
perl -d script.pl
For Mojolicious:
MOJO_REACTOR=POLL perl -d script/app.pl daemon -l http://0.0.0.0:5001
Breakpoints
Set a breakpoint at a specific line:
b 42
Set a breakpoint in another file:
b MyModule.pm:10
Break on subroutine entry:
b subname
List breakpoints:
B
Delete a breakpoint:
B 1
Delete all breakpoints:
B *
Force a breakpoint inside code:
BEGIN { $DB::single = 1 }
Stepping Commands
Step into:
s
Step over:
n
Step out of current subroutine:
r
Continue execution:
c
Inspecting Variables
Print a scalar:
p $var
Dump a variable (arrays, hashes, objects):
x $var
Dump a reference:
x \%ENV
Show stack trace:
T
Quitting the Debugger
Quit the debugger:
q
Force quit (even if Perl warns about active subs):
q!
Useful Tips
- Use MOJO_REACTOR=POLL when debugging Mojolicious to avoid forking issues.
- Use BEGIN { $DB::single = 1 } to simulate breakpoints.
- Use x instead of p for complex structures.
Example
Given script/app.pl
1 #!/usr/bin/env perl
2 use Mojolicious::Lite;
3 use feature 'signatures';
4 no warnings 'experimental::signatures';
5 use DBI;
6
7 helper db => sub {
8 state $dbh = DBI->connect(
9 "dbi:Pg:dbname=dvdrental;host=192.168.1.220",
10 "rest_user",
11 "H1stage5!!!!",
12 { RaiseError => 1, AutoCommit => 1 }
13 );
14 };
15
16 get '/films' => sub ($c) {
17 my $sth = $c->db->prepare("SELECT film_id, title FROM film LIMIT 10");
18 $sth->execute;
19 $c->render(json => $sth->fetchall_arrayref({}));
20 };
21
22 app->start;
To add a breakpoint a line 17 <syntaxhight lang="text"> b script/app.pl:17 </syntaxhighlight>
Setting a Breakpoint Before the Debugger Starts
You can force the Perl debugger to stop at a specific point *before* the script begins normal execution by inserting a `BEGIN` block with the debugger flag:
BEGIN { $DB::single = 1 }
Because `BEGIN` blocks run at compile time, the debugger halts immediately when the script loads.
Example: Break Before Line 17
#!/usr/bin/env perl
use Mojolicious::Lite;
use feature 'signatures';
no warnings 'experimental::signatures';
use DBI;
helper db => sub {
state $dbh = DBI->connect(
"dbi:Pg:dbname=dvdrental;host=192.168.1.220",
"rest_user",
"H1stage5!!!!",
{ RaiseError => 1, AutoCommit => 1 }
);
};
get '/films' => sub ($c) {
BEGIN { $DB::single = 1 } # Breakpoint before line 17
my $sth = $c->db->prepare("SELECT film_id, title FROM film LIMIT 10");
$sth->execute;
$c->render(json => $sth->fetchall_arrayref({}));
};
app->start;
This causes the debugger to stop before executing the line that prepares the SQL statement.
Package Management
Which package manager?
Perl modules can come from your OS or from CPAN.
- OS packages (e.g. `apt install libmojolicious-perl`): stable, integrated with the distro; best for tools your whole system depends on.
- CPAN + `cpan` client: ships with Perl; interactive shell for installing modules from CPAN.
- CPAN + `cpanm` (cpanminus): lightweight command-line client; common choice for application projects, especially with `local::lib` or Carton.
For project code, prefer `cpanm` + a `cpanfile` over installing into the system Perl.
Listing what you have
Some ways to see what is installed or where it came from:
# Show where a module is loaded from
perl -MData::Dumper -e 'print $INC{"Data/Dumper.pm"}, "\n";'
# Per-module location via perldoc
perldoc -l Mojolicious
# Distro-level Perl modules (Debian/Ubuntu)
apt list --installed '*perl*'
Installing modules
Using `cpanm` (recommended for app code):
# Install a module into your local::lib tree
cpanm Mojolicious
# Install all dependencies for a project described in cpanfile
cpanm --installdeps .
Using the core `cpan` client:
cpan Mojolicious
Using the OS package manager (Debian/Ubuntu example):
sudo apt install libmojolicious-perl
Removing modules
Perl does not have a universal uninstall command for all clients, so strategy depends on how you installed:
- OS packages: use the package manager (e.g. `sudo apt remove libmojolicious-perl`).
- `local::lib` tree: you can safely remove or recreate the whole `/home/iwiseman/perl5` tree if it only holds project-local modules.
- `cpanm`: recent versions support uninstall for some cases, but many people simply clean and reinstall into a fresh `local::lib`.
Searching for modules
Most searching is done via websites, but you can also search from the shell.
# Search on MetaCPAN (browser)
https://metacpan.org
# Search in the legacy CPAN shell
cpan
cpan[1]> m /Mojo/
Avoiding and resolving conflicts
- Avoid mixing system packages and CPAN installs for the same module (choose one source per module).
- Use `local::lib` (and optionally Carton + `cpanfile`) so each project has its own module versions.
- If things get messy in your local tree, it is often easier to remove `/home/iwiseman/perl5` and reinstall the modules you actually need.
Useful sites
- MetaCPAN: https://metacpan.org – modern search and documentation for CPAN modules.
- CPAN: https://www.cpan.org – canonical archive of Perl distributions.
- BackPAN: https://backpan.perl.org – historical archive of old distribution versions (usually not needed day-to-day).
Types
Perl has scalars, arrays, and hashes. Scalars can hold strings, numbers, and references.
- Scalars: numbers, strings, references
- Arrays: ordered lists
- Hashes: key/value maps
Numbers
Perl uses numeric context for arithmetic.
my $one = 1; # integer
my $pi = 3.14159; # float
my $big = 1_000_000; # underscore for readability
Strings
Double quotes interpolate variables and escapes; single quotes do not.
my $name = "Perl";
my $greet = "Hello, $name\n";
my $raw = 'Hello, $name\n';
print $greet;
print $raw;
Arrays
Arrays are ordered lists.
my @numbers = (1, 2, 3, 4, 5);
print $numbers[2]; # 3
Hashes
Hashes map keys to values.
my %ages = (
Alice => 30,
Bob => 25,
);
print $ages{Alice};
Collections
Filtering and Mapping
Perl uses grep for filtering and map for transforms.
my @ints = (1, 2, 3, 4, 5);
my @small = grep { $_ < 4 } @ints;
my @squares = map { $_ * $_ } @ints;
Combining
my @ints = (1, 2, 3, 4, 5);
my @small_squares = map { $_ * $_ }
grep { $_ < 4 } @ints;
Flow Control
If
if ($answer eq $correct_answer) {
print "You were correct\n";
} else {
print "Try again\n";
}
Given/When
Perl supports a switch-like feature via given/when (use with feature). Note: it is experimental in some versions.
use feature 'switch';
my $number = 3;
given ($number) {
when (0) { print "Invalid number\n"; }
when ([1,2]) { print "Number too low\n"; }
when (3) { print "Number correct\n"; }
when (4) { print "Number too high, but acceptable\n"; }
default { print "Number too high\n"; }
}
For and Foreach
for (my $i = 0; $i < 5; $i++) {
print $i;
}
foreach my $n (@numbers) {
print $n;
}
Community Notes
Popular Today
- Perl 5 – the mainline Perl most people mean when they say “Perl”; actively maintained, widely deployed in sysadmin, web, data-processing and scripting.
- Mojolicious – modern, non-blocking web framework (routers, templates, testing helpers, WebSockets, etc.); good default choice for new web apps.
- DBI / DBD::* – standard way to talk to databases (e.g. `DBD::Pg` for Postgres); forms the base for higher-level ORMs.
- Carton / cpanminus – common tools to manage project-specific dependencies from CPAN (`cpanm`) and lockfiles (`Carton`) without touching system Perl.
Less Popular / Legacy
- Perl 6 / Raku – Perl 6 was renamed to Raku and is now its own language and community; interesting, but not a path for “upgrading” Perl 5 code.
- CGI.pm – classic module for raw CGI-style web programming; still works, but most new code uses PSGI/Plack or frameworks like Mojolicious instead.
- mod_perl-only stacks – historically popular for embedding Perl in Apache; many new projects prefer FastCGI, PSGI/Plack, or Mojolicious daemons behind nginx/Apache.
Starting a Real Project: dvdrental_api_pl
This section sketches how to go from an empty directory to the current dvdrental_api_pl REST API setup. Details of routes and database schema can live on a separate page.
1. Create the project skeleton
mkdir -p dvdrental_api_pl/{lib,script}
cd dvdrental_api_pl
# Optional: initialise git
git init
Recommended layout for this project:
- script/app.pl – Mojolicious application entry point (matches the VS Code debug config).
- lib/DVD/... – your application modules (e.g. DVD::API, DVD::Model::Rental).
- Documentation/ – wiki pages like this one.
2. Set up local Perl environment
Use local::lib (or similar) so project modules do not touch the system Perl.
perl -Mlocal::lib >> ~/.bashrc
source ~/.bashrc
After that, verify the environment variables look like the ones described in the local::lib section above (PATH, PERL5LIB, etc.).
3. Install project dependencies
Use cpanm with a cpanfile (recommended). A minimal cpanfile for a Mojolicious + Postgres REST API might look like:
requires 'Mojolicious';
requires 'DBI';
requires 'DBD::Pg';
requires 'JSON::MaybeXS';
Then install:
cpanm --installdeps .
You can also mix in OS packages for heavy C libraries (e.g. libpq-dev) as shown in the Package Installation section.
4. Create a minimal Mojolicious REST stub
Create script/app.pl to expose at least one test route.
#!/usr/bin/env perl
use strict;
use warnings;
use Mojolicious::Lite;
get '/health' => sub ($c) {
$c->render(json => { status => 'ok' });
};
app->start;
Run it manually to confirm it works:
perl script/app.pl daemon -l http://0.0.0.0:5001
curl http://localhost:5001/health
Later, this stub can be replaced by a full application class living under lib/DVD/..., but this is enough to validate the toolchain.
5. Hook up VS Code debugging
Use the launch configuration shown earlier ("Perl Debug App") so VS Code runs script/app.pl in daemon mode:
- Program: ${workspaceFolder}/script/app.pl
- Args: ["daemon", "-l", "http://0.0.0.0:5001", "-m", "production"]
- Env: MOJO_REACTOR=POLL
Set a breakpoint inside the /health route and verify that requests from curl or a browser hit the breakpoint.
6. Connect to the dvdrental database
Once the basic REST skeleton works, add a module under lib/DVD/ that uses DBI/DBD::Pg to talk to the dvdrental Postgres database. A separate page can describe:
- Connection DSN and credentials.
- Simple SELECT examples.
- How routes map to SQL queries.
At this point you have:
- A local Perl environment (local::lib).
- Dependencies managed via cpanm/cpanfile.
- A Mojolicious app entry script with debug support in VS Code.
- A place (lib/DVD/...) to grow the dvdrental_api_pl REST API.