Complete Reference Guide

Learning Perl

A comprehensive, structured outline of the Perl programming language — from first principles through advanced mastery. Every major feature, idiom, and best practice in one document.

24 chapters Perl 5.x learning.perl · intermediate.perl · modern.perl O'Reilly canon
01 Introduction & Philosophy

What is Perl?

Perl (Practical Extraction and Report Language) is a high-level, general-purpose, interpreted, dynamic programming language created by Larry Wall in 1987. It draws from C, shell scripting, awk, sed, Lisp, and natural language, combining them into a flexible tool especially suited for text manipulation, system administration, and rapid development.

The TMTOWTDI Principle "There's More Than One Way To Do It" — Perl's guiding philosophy. Unlike Python's "one obvious way," Perl gives programmers multiple equally valid approaches. This is both its great strength and the source of most criticism.

Perl's Strengths

Text processing System administration Regular expressions Rapid prototyping CPAN ecosystem Bioinformatics Web / CGI (legacy) Glue language One-liners

Installing & Running Perl

terminalshell
perl --version              # check your version
perl script.pl              # run a script
perl -e 'say "Hello"'       # inline one-liner
perl -c script.pl           # syntax check only
perl -w script.pl           # enable warnings (old way)
perl -d script.pl           # interactive debugger
perl -de 42                 # REPL-like debugger session
TipUse perlbrew or plenv to manage multiple Perl versions on one machine without touching the system Perl.

The Three Virtues of a Programmer (Larry Wall)

VirtueMeaning
LazinessWrite less total work by automating; write code others can reuse
ImpatienceHate waiting; write programs that anticipate your needs
HubrisWrite code you won't be ashamed of; take ownership of quality
02 Program Structure

Anatomy of a Perl Script

hello.plperl
#!/usr/bin/env perl       # shebang — tells OS to run with Perl

use strict;                # require all variables to be declared
use warnings;             # warn about suspicious constructs
use feature 'say';       # enable say(), state, etc.
use utf8;                 # source is UTF-8 encoded
use open ':std', ':utf8'; # STDIN/OUT/ERR in UTF-8

my $greeting = "Hello, world!";
say $greeting;

exit 0;                   # explicit exit code (optional)

Key Pragmas

PragmaPurposeEssential?
use strictEnforce variable declarations; ban barewords and symbolic refsYes — always
use warningsWarn about uninitialized vars, wrong types, deprecated usageYes — always
use feature 'say'Enable say (print + newline), state, switchRecommended
use feature ':5.36'Enable all features introduced by a specific Perl versionModern style
use v5.36Shorthand: declares minimum version + enables all its featuresModern style
use utf8Source file uses UTF-8; allows Unicode identifiersIf using Unicode
use constantDefine compile-time constants (inlineable)When needed
use EnglishHuman-readable aliases for punctuation variables ($ARG, $OS_ERROR…)Optional
use CarpBetter error messages that point to the caller, not the calleeIn modules
use Data::DumperPretty-print data structures for debuggingDebug only

Statements, Blocks & Comments

syntaxperl
# Single-line comment — everything after # is ignored
# (except inside strings)

my $x = 42;                 # every statement ends with ;
my $y = 10;

{                           # braces create a new lexical scope
    my $local = $x + $y;    # $local is only visible inside {}
    say $local;
}
# $local is gone here

# Multi-line "comment" using a heredoc trick:
=pod
This block is POD documentation — ignored by the compiler.
It ends with =cut.
=cut
WarningUnlike C, Perl requires curly braces even for single-statement if / while blocks. There is no braceless one-liner form.
03 Scalars & Strings

Scalar Variables

A scalar holds exactly one value: a string, number, reference, or undef. The $ sigil always means "give me one thing." Perl converts between strings and numbers automatically based on context.

scalars.plperl
my $name  = "Alice";       # string
my $age   = 30;             # number
my $pi    = 3.14159;        # float
my $empty = undef;         # no value yet
my $flag  = 1;              # true (no boolean type)

# Perl auto-converts based on context:
my $s  = "42 items";
my $n  = $s + 1;  # numeric context → 43 (uses "42" part)
my $s2 = $n . "!"; # string context → "43!"

String Quoting

SyntaxInterpolates?Description
'single'NoCompletely literal — only \' and \\ are special
"double"YesVariables and \n \t \x{} \N{} escape sequences expanded
q(text)NoSame as single quotes; any delimiter: q|..| q{..}
qq(text)YesSame as double quotes; any delimiter: qq|..| qq{..}
qw(a b c)NoList of whitespace-separated words → ('a','b','c')
`command`YesBacktick: runs shell command and returns its output
qx(command)YesSame as backticks; safer with unusual chars in command

String Operators

OperatorMeaningExample
.Concatenate"foo" . "bar""foobar"
xRepeat"ab" x 3"ababab"
.=Concatenate and assign$s .= " more"
eq ne lt gt le ge cmpString comparison"foo" eq "foo" → true

String Functions

FunctionDescription
length($str)Number of characters
substr($str, $off, $len)Extract substring; with 4th arg: replace in-place
index($str, $sub)Position of first occurrence (-1 if not found)
rindex($str, $sub)Position of last occurrence
uc($str) / lc($str)Upper / lower case entire string
ucfirst / lcfirstChange case of first character only
chomp($str)Remove trailing \n (or $/); modifies in-place; returns count
chop($str)Remove and return the last character
reverse($str)In scalar context: reverses the string
sprintf($fmt, @args)Format string without printing; same directives as C printf
split(/pat/, $str, $lim)Split string on pattern → array
join($sep, @list)Join array elements into a string with separator
pos($str)Current match position after /g match

Heredocs

heredoc.plperl
# Interpolating heredoc (like double quotes)
my $name = "Alice";
my $text = <<END;
Hello, $name.
Welcome to Perl.
END

# Non-interpolating (like single quotes) — quote the label
my $raw = <<'END';
Literal: $name is not expanded here.
END

# Indented heredoc (Perl 5.26+) — ~ strips leading whitespace
my $indented = <<~END;
    This content can be indented
    to match surrounding code.
    END

Truthiness in Perl

Critical RuleThese values are false: undef, 0, "" (empty string), "0". Everything else is true — including "00", "0.0", and the string "false".
04 Numbers & Operators

Number Literals

numbers.plperl
42           # integer
3.14         # float
6.02e23      # scientific notation
0xFF         # hex (255)
0b1010       # binary (10)
0777         # octal (511)
1_000_000    # underscores for readability
0x1F_A0      # underscores in hex too

Arithmetic Operators

OpMeaningNotes
+ - * /Basic arithmeticStandard, always numeric
%ModuloRemainder after integer division
**Exponentiation2 ** 10 = 1024. Not ^ (that's bitwise XOR)
++ --Auto-increment/decrementOn strings: "aa"+1"ab"; "Az"+1"Ba"
abs, int, sqrtBuilt-in mathint truncates (not rounds)

Comparison Operators

Two complete sets — never mix them== converts both sides to numbers first. eq compares as strings. "foo" == 0 is TRUE (both become 0). "10" == "10.0" is TRUE. "10" eq "10.0" is FALSE.
NumericStringReturns
==eqEqual
!=neNot equal
<ltLess than
>gtGreater than
<=leLess than or equal
>=geGreater than or equal
<=>cmp-1, 0, or 1 (spaceship)

Logical Operators — Two Syntaxes

logical.plperl
# Symbol forms — HIGH precedence (use in expressions)
$a && $b       # true if both are true
$a || $b       # true if either is true
!$a           # negation

# Word forms — LOW precedence (use at statement level)
$a and $b     # same as &&, but lower precedence
$a or $b      # same as ||, but lower precedence
not $a        # same as !, but lower precedence

# Defined-or (5.10+) — only checks definedness, not truth
$val // "default"    # use "default" if $val is undef
$val //= "default"   # assign default if undef

# Classic idioms
open my $fh, '<', $f or die $!;   # or-die
my $x = $input || "fallback";    # or-default
05 Operator Precedence

Operators are listed from highest precedence (binds tightest) to lowest. When in doubt, add parentheses — it costs nothing and aids readability.

22-> term ()Method call, subscript, grouping (highest)
21++ --Auto-increment / decrement
20**Exponentiation (right-associative)
19! ~ \ + -Unary not, bitwise complement, ref, unary plus/minus
18=~ !~Regex binding
17* / % xMultiply, divide, modulo, string repeat
16+ - .Add, subtract, concatenate
15<< >>Bit shift left / right
14named unary-f file tests, chr, hex, lc, etc.
13< > <= >= lt gt le geNumeric and string ordering
12== != <=> eq ne cmp ~~Equality and spaceship
11&Bitwise AND
10| ^Bitwise OR / XOR
9&&Logical AND (short-circuit)
8|| //Logical OR / defined-or (short-circuit)
7..Range operator (list / flip-flop)
6?:Ternary conditional (right-associative)
5= += -= .= //= …Assignment (right-associative)
4, =>Comma / fat comma
3list operators (left side)print, sort, die, etc.
2notLogical NOT (word form — very low)
1and or xorLogical AND/OR/XOR (word form — lowest)
06 Arrays

Declaration & Basic Access

Sigil RuleThe array is @arr. One element is $arr[i] — sigil shifts to $ because you're extracting a scalar. A slice is @arr[1,3] — sigil stays @ because you're extracting a list.
arrays.plperl
my @fruits  = ("apple", "banana", "cherry");
my @words   = qw(foo bar baz);   # quote-words shorthand
my @numbers = (1..10);           # range operator
my @empty   = ();               # empty array

$fruits[0]      # "apple" — zero-indexed
$fruits[-1]     # "cherry" — last element
$fruits[-2]     # "banana" — second to last
$#fruits        # 2 — index of last element
scalar @fruits  # 3 — element count (also: $n = @fruits)

# Slices
my @first_two = @fruits[0,1];     # explicit indices
my @slice     = @fruits[0..1];     # range slice

Modifying Arrays

FunctionEffectReturns
push @a, @valsAppend one or more values to endNew element count
pop @aRemove and return last elementRemoved element
unshift @a, @valsPrepend one or more values to frontNew element count
shift @aRemove and return first elementRemoved element
splice(@a, $off, $len, @new)Remove $len elements at offset, insert @newRemoved elements
delete $a[$i]Replace element with undef (array length unchanged)Deleted value

Sorting

sorting.plperl
sort @arr                        # alphabetical (default)
sort { $a <=> $b } @arr          # numeric ascending
sort { $b <=> $a } @arr          # numeric descending
sort { lc($a) cmp lc($b) } @arr  # case-insensitive string
reverse sort @arr               # reverse alphabetical

# Schwartzian Transform: sort by expensive-to-compute key
my @sorted = map  { $_->[0] }          # 3. strip key
             sort { $a->[1] <=> $b->[1] }  # 2. sort by key
             map  { [$_, compute_key($_)] }  # 1. attach key
             @arr;
07 Hashes

Declaration & Access

hashes.plperl
my %person = (
    name  => "Alice",    # => is "fat comma" — auto-quotes left side
    age   => 30,
    city  => "Austin",
);

$person{name}         # "Alice" — curly braces for hash access
$person{"my key"}     # quoted key for non-bareword keys
$person{missing}      # undef (no warning by default)

# Hash slice
my @vals = @person{qw(name age)};  # sigil = @ for slice

# Hash in list context = flattened k/v pairs
my @pairs = %person;   # ("name","Alice","age",30,...) — arbitrary order

Hash Functions

Function / OpDescription
keys %hList of all keys (arbitrary order — use sort)
values %hList of all values (same order as keys)
each %hReturns next (key, value) pair; use in while loop
exists $h{k}True if key exists (even if value is undef)
defined $h{k}True if value for key is not undef
delete $h{k}Remove key/value pair; returns the deleted value
delete @h{@keys}Delete a slice of keys at once
scalar %hNumber of key/value pairs (Perl 5.26+); older Perls: "X/Y"

Common Hash Patterns

hash_patterns.plperl
# Counting occurrences
my %count;
$count{$_}++ for @words;

# Lookup set (membership test)
my %is_valid = map { $_ => 1 } qw(red green blue);
say "valid" if $is_valid{$color};

# Invert a hash (swap keys and values)
my %reverse = reverse %original;

# Merge two hashes (right-side wins on collision)
my %merged = (%defaults, %overrides);

# Iterate in sorted key order
for my $key (sort keys %h) {
    printf "%-12s => %s\n", $key, $h{$key};
}
08 References

What is a Reference?

A reference is a scalar value that holds the memory address of another value — like a pointer in C. References are how you pass large data without copying, build nested structures, and store subroutines in variables.

Creating References

refs.plperl
# \ operator: reference to an existing variable
my $sref = \$scalar;    # scalar ref
my $aref = \@array;     # array ref
my $href = \%hash;      # hash ref
my $cref = \&mysub;    # code ref

# Anonymous constructors: create data directly as a ref
my $aref = [1, 2, 3];            # anonymous arrayref
my $href = {name => "Alice"};     # anonymous hashref
my $cref = sub { $_[0] * 2 };     # anonymous sub (lambda)

Dereferencing

deref.plperl
# Arrow notation — preferred for readability
$aref->[0]           # array element via ref
$href->{name}         # hash value via ref
$cref->(@args)        # call a code ref
$$sref                # dereference scalar ref

# Block dereference — dereference to full structure
@{$aref}             # whole array
%{$href}             # whole hash
@{$aref}[1,3]        # slice from arrayref
@{$href}{qw(a b)}    # slice from hashref

# Adjacent brackets: arrow is optional between brackets
$aref->[0]{key}        # same as $aref->[0]->{key}

# ref() — identify the type of a reference
ref($aref)    # "ARRAY"
ref($href)    # "HASH"
ref($cref)    # "CODE"
ref($obj)     # "ClassName" for blessed objects
ref($plain)  # "" (empty string) — not a reference

Dispatch Tables

dispatch.plperl
# Hash of code refs — replaces long if/elsif chains
my %actions = (
    add  => sub { $_[0] + $_[1] },
    sub  => sub { $_[0] - $_[1] },
    mul  => sub { $_[0] * $_[1] },
);
my $result = $actions{$op}->($a, $b);
09 Complex Data Structures

Common Patterns

data_structures.plperl
# Array of Hashrefs (AoH) — the most common pattern
my @people = (
    { name => "Alice",  age => 30 },
    { name => "Bob",    age => 25 },
);
$people[0]{name}    # "Alice"
$people[1]{age}     # 25

# Hash of Arrayrefs (HoA)
my %scores = (
    alice => [92, 88, 95],
    bob   => [74, 80, 65],
);
$scores{alice}[0]  # 92
push @{$scores{bob}}, 91;

# Hash of Hashrefs (HoH)
my %registry = (
    alice => { email => 'a@b.com', role => 'admin' },
    bob   => { email => 'b@b.com', role => 'user'  },
);
$registry{alice}{email}   # 'a@b.com'

# Array of Arrayrefs — 2D matrix
my @matrix = ([1,2,3], [4,5,6], [7,8,9]);
$matrix[1][2]       # 6 (row 1, col 2)

# Deeply nested: array of hashrefs with nested arrays
my @classes = (
    { name    => "Math",
      students => [ {name => "Alice", grade => 'A'},
                    {name => "Bob",   grade => 'B'} ], },
);
$classes[0]{students}[0]{name}   # "Alice"
TipUse Data::Dumper or Devel::Dumper to print any complex structure during development: use Data::Dumper; print Dumper(\@classes);
10 Control Flow

Conditionals

block formperl
if ($x > 0) {
    say "positive";
} elsif ($x == 0) {
    say "zero";
} else {
    say "negative";
}

# unless = "if not"
unless ($done) {
    work();
}
postfix & ternaryperl
# Postfix — condition after statement
say "yes" if     $flag;
say "no"  unless $flag;
return    unless defined $val;

# Ternary: COND ? TRUE : FALSE
my $label = $n > 0
    ? "positive"
    : "non-positive";

# Chained ternary
my $g =   $s>=90 ? 'A'
        : $s>=80 ? 'B'
        : $s>=70 ? 'C'
        :          'F';
NoteIt is elsif, not else if or elif. Curly braces are always required — there is no braceless form.
11 Loops

All Loop Forms

loops.plperl
# while: test first, run while true
while ($i < 10)  { $i++ }

# until: test first, run while FALSE
until ($done)    { work() }

# do/while: always runs body at least once
do {
    $input = <STDIN>; chomp $input;
} while ($input ne 'quit');

# C-style for
for (my $i=0; $i<10; $i++) { say $i }

# foreach (for and foreach are identical keywords)
for my $item (@list)    { say $item   }
foreach my $i (1..10)  { say $i     }

# $_ as implicit variable (no 'my $item' needed)
for (@list)             { say       }  # say prints $_

# Postfix for — compact one-liners
say "  $_" for @list;
say $_*2  for 1..5;

# Loop control
last;       # break — exit loop immediately
next;       # continue — skip to next iteration
redo;       # restart current iteration without re-evaluating condition

# Labels: control outer loop from inner loop
OUTER: for my $i (1..5) {
    for my $j (1..5) {
        last OUTER if $i + $j > 7;
    }
}
12 Subroutines

Defining & Calling

subs.plperl
# Basic subroutine — arguments arrive in @_
sub greet {
    my ($name, $greeting) = @_;   # always unpack @_ first
    $greeting //= "Hello";        # default value with defined-or
    return "$greeting, $name!";
}

greet("Alice");            # "Hello, Alice!"
greet("Bob", "Hi");         # "Hi, Bob!"

# Named parameters (hash style — very common)
sub make_user {
    my (%args) = @_;
    my $name  = $args{name}  // die "name required";
    my $role  = $args{role}  // "user";
    return { name => $name, role => $role };
}
make_user(name => "Alice", role => "admin");

# Multiple return values — just return a list
sub minmax {
    my (@nums) = @_;
    return (min(@nums), max(@nums));
}
my ($lo, $hi) = minmax(3,1,4,1,5);

# Context-sensitive return with wantarray()
sub context_aware {
    return wantarray ? (1,2,3) : "summary";
}

Argument Passing Gotchas

Arrays flatten into @_If you pass two arrays f(@a, @b), they merge into a single flat list in @_. Pass references instead: f(\@a, \@b). Then unpack: my ($aref, $bref) = @_;
NotePerl subroutines do not support true prototypes for type enforcement the way C does. The prototype feature exists but is rarely used in modern code. Named parameters via hashes is the idiomatic solution.
13 Scope & Closures

Three Scoping Keywords

KeywordTypeVisibilityLifetime
myLexicalEnclosing { } block onlyUntil block ends (or longer if closed over)
ourPackage globalEntire package / file (and importers)Entire program run
localDynamicCurrent call stack frame (and callees)Restored when enclosing block exits
scope.plperl
my $x = "outer";
{
    my $x = "inner";  # shadows outer — separate variable
    say $x;           # "inner"
}
say $x;               # "outer"

# local: temporarily replaces a package variable
our $sep = ",";
sub with_pipe_sep {
    local $sep = "|";  # $sep is "|" here AND in all functions called from here
    print_items();
}                     # $sep restored to "," here

Closures

A closure is a subroutine that captures ("closes over") variables from its enclosing lexical scope. The variables live as long as the closure does — even after the outer function returns.

closures.plperl
# Factory function: creates specialised closures
sub make_counter {
    my ($start) = @_;
    my $count   = $start // 0;
    return sub {
        return $count++;   # $count is captured — persists
    };
}

my $c1 = make_counter(0);
my $c2 = make_counter(10);
$c1->();  # 0   — each counter has its own $count
$c1->();  # 1
$c2->();  # 10

# state: per-call persistent variable (no factory needed)
use feature 'state';
sub auto_id {
    state $n = 0;   # initialized once; kept across calls
    return ++$n;
}
14 Regular Expressions

Operators

regex.plperl
$str =~  /pattern/          # match — true if $str contains pattern
$str !~  /pattern/          # negated match
if (/pattern/)             # implicit $_ =~ /pattern/

$str =~ s/old/new/          # substitution (first match)
$str =~ s/old/new/g         # global: replace all
my $copy = $str =~ s/a/b/gr # /r: return modified copy
$str =~ s/(\d+)/$1*2/ge     # /e: evaluate replacement as code

$str =~ tr/a-z/A-Z/         # transliterate (char mapping)
my $n = ($str =~ tr/aeiou//) # count vowels (no replacement)
$str =~ tr/aeiou//d          # delete vowels
$str =~ tr/a-z//s            # /s: squeeze repeated translated chars

# Compiled regex (use in multiple places)
my $re = qr/\d{4}-\d{2}-\d{2}/;
$str =~ $re;

Modifiers

FlagMeaning
/iCase-insensitive matching
/gGlobal — find all occurrences; in list context returns all matches
/mMultiline — ^ and $ match at line boundaries, not just string
/sSingle-line — . also matches \n
/xExtended — whitespace and #comments ignored in pattern (for readable regex)
/rReturn modified copy; do not modify the original string
/eEvaluate replacement as Perl code (substitute only)
/oCompile pattern once (optimization, rarely needed)

Capture Groups & Variables

captures.plperl
# Positional captures: $1, $2, ...
if ($date =~ /(\d{4})-(\d{2})-(\d{2})/) {
    my ($year, $mon, $day) = ($1, $2, $3);
}

# Named captures: (?<name>...) → $+{name}
$date =~ /(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/;
say $+{y}, $+{m}, $+{d};

# List context /g: capture all matches at once
my @words  = ($text =~ /\b\w+\b/g);
my @pairs  = ($text =~ /(\w+)=(\w+)/g);  # k,v,k,v,...

# Non-capturing group: (?:...) — group without $1
/(?:foo|bar)(\d+)/

# Regex match variables
$&  # the entire matched string
$`  # everything BEFORE the match
$'  # everything AFTER the match
$+  # text matched by the last bracket

Metacharacters Quick Reference

PatternMatches
.Any character except newline (use /s to include \n)
\d \DDigit [0-9] / non-digit
\w \WWord char [a-zA-Z0-9_] / non-word
\s \SWhitespace [\t\n\r\f ] / non-whitespace
^ $Start / end of string (or line in /m)
\A \z \ZAbsolute string start / end / end-before-optional-newline
\b \BWord boundary / non-word boundary
* + ?0+, 1+, 0 or 1 (all greedy); add ? for non-greedy: *? +? ??
{n} {n,} {n,m}Exactly n, at least n, between n and m times
[abc] [^abc]Character class / negated character class
a|bAlternation — matches a or b
(?=…) (?!…)Positive / negative lookahead (zero-width)
(?<=…) (?<!…)Positive / negative lookbehind (zero-width)
(?>…)Atomic group — prohibits backtracking
15 List Processing

map, grep, sort

These three functions process lists functionally — they never modify the original list and are the foundation of idiomatic Perl.

list_ops.plperl
# map: transform each element ($_ = current element)
my @doubled  = map  { $_ * 2      } @numbers;
my @names    = map  { $_->{name} } @people;
my %lookup   = map  { $_ => 1     } @keys;   # build lookup hash

# grep: filter (keep elements where block is true)
my @evens    = grep { $_ % 2 == 0    } @numbers;
my @admins   = grep { $_->{role} eq 'admin' } @users;
my @nocomment = grep { !/^#/           } @lines;

# sort: with a comparator block
my @by_name  = sort { $a->{name} cmp $b->{name} } @people;
my @by_score = sort { $b->{score} <=> $a->{score} } @people;

# Chaining — reads as a pipeline
my @result =
    sort { $a <=> $b }
    grep { $_ > 5 }
    map  { $_ ** 2 }
    @numbers;

List::Util Functions

FunctionDescription
sum(@list)Sum of all elements; sum0 returns 0 for empty list
max(@list) / min(@list)Largest / smallest element
first { } @listFirst element where block is true
any { } @listTrue if block is true for any element
all { } @listTrue if block is true for all elements
none { } @listTrue if block is true for no elements
reduce { } @listReduce list to single value; $a and $b are accumulator/element
uniq(@list)Remove duplicates (preserving order)
shuffle(@list)Return list in random order
16 File I/O

Opening Files

fileio.plperl
# Always use three-argument open!
open(my $fh, '<',   'file.txt')  or die $!;   # read
open(my $fh, '>',   'out.txt')   or die $!;   # write (truncate)
open(my $fh, '>>',  'log.txt')   or die $!;   # append
open(my $fh, '+<',  'rw.txt')   or die $!;   # read-write

# With encoding layer
open(my $fh, '<:utf8', $path)  or die $!;
# In-memory file (open against a variable)
open(my $fh, '>', \my $buf)  or die $!;

Reading Files

reading.plperl
# Line by line — most memory-efficient
while (my $line = <$fh>) {
    chomp $line;    # remove trailing \n
    # process $line
}

# Slurp all lines into an array
my @lines = <$fh>;
chomp @lines;

# Slurp entire file into a string
my $content = do { local $/; <$fh> };

# Diamond <>: reads ARGV files or STDIN
while (<>) { chomp; process($_) }

File Test Operators

TestMeaning
-e $pathFile or directory exists
-f $pathIs a plain file (not directory, symlink…)
-d $pathIs a directory
-l $pathIs a symbolic link
-r / -w / -xReadable / writable / executable by current user
-s $pathFile size in bytes (0 if empty)
-z $pathFile is empty (zero size)
-T / -BText file / Binary file (heuristic)
-M / -A / -CAge in days: last modified / accessed / inode changed

Directory Operations

dirs.plperl
opendir(my $dh, ".") or die $!;
my @files = grep { !/^\./ } readdir($dh);  # exclude dotfiles
closedir($dh);

use File::Find;     # recursive directory walk
use File::Glob;     # glob patterns
use File::Path qw(make_path remove_tree);  # mkdir -p / rm -rf
use File::Basename  qw(dirname basename);   # path components
use File::Spec;     # portable path manipulation
17 Error Handling

die, warn, eval

errors.plperl
# die: throw exception (exits program if not caught)
die "Something failed\n";        # \n suppresses "at line N"
die "Error: $!\n";              # $! = system error message
die { type=>"NotFound", msg=>"..." };  # structured exception object

# warn: print warning to STDERR, continue execution
warn "Something looks wrong\n";

# eval { }: catch exceptions (Perl's try block)
my $result = eval {
    risky_operation();
    "success";           # return value if no exception
};

if (my $e = $@) {         # $@ holds the caught exception
    if (ref($e) eq 'HASH') {
        say "Caught: $e{type}: $e{msg}";
    } else {
        die $e;             # re-throw unknown exceptions
    }
}

# Carp module: better caller-perspective error messages
use Carp qw(carp croak confess cluck);
croak    "bad input";     # like die, but blame the caller
carp     "suspicious";  # like warn, blame the caller
confess  "deep error";  # die + full stack trace
cluck    "soft error";  # warn + full stack trace

Exception Objects with Exception::Class

exceptions.plperl
use Exception::Class (
    'MyApp::Error'             => { description => 'Base error' },
    'MyApp::Error::IO'         => { isa => 'MyApp::Error',
                                    fields => ['filename'] },
);

eval {
    MyApp::Error::IO->throw(
        message  => "Cannot open file",
        filename => $path,
    );
};
if (my $e = $@) {
    if ($e->isa('MyApp::Error::IO')) {
        say "IO error on " . $e->filename;
    }
}
18 Object-Oriented Perl

The Three Primitives

How Perl OOP worksA package is a class. A blessed reference (any ref tied to a package name via bless()) is an object. A subroutine in the package is a method — called with $obj->method(). That's the entire object system. Everything else is convention built on these three rules.
oop.plperl
package Animal;
use strict; use warnings;

# Constructor — 'new' is conventional, any name works
sub new {
    my ($class, %args) = @_;   # $class = "Animal" (the package name)
    my $self = {
        name  => $args{name}  // "Unknown",
        sound => $args{sound} // "...",
    };
    return bless $self, $class;   # bless ties $self to class
}

# Accessor method (get/set)
sub name {
    my ($self, $new) = @_;
    $self->{name} = $new if defined $new;
    return $self->{name};
}

# Regular method
sub speak {
    my ($self) = @_;
    printf "%s says %s\n", $self->{name}, $self->{sound};
}

# Subclass
package Dog;
use parent 'Animal';       # inherit from Animal

sub new {
    my ($class, %args) = @_;
    $args{sound} //= "Woof";
    return $class->SUPER::new(%args);  # call parent constructor
}

package main;
my $dog = Dog->new(name => "Rex");
$dog->speak();              # "Rex says Woof"
ref($dog);                  # "Dog"
$dog->isa('Animal');        # 1 (true)
$dog->can('speak');         # coderef or undef

Modern OOP: Moose & Moo

TipFor real applications, use Moose (full-featured, slower) or Moo (lightweight, fast). They provide attribute declarations, type constraints, roles (mixins), method modifiers, and lazy initialization — eliminating nearly all boilerplate.
moo_example.plperl
package Person;
use Moo;

has 'name'  => (is => 'rw', required => 1);
has 'email' => (is => 'rw');
has 'age'   => (is => 'ro', default => 0);

sub greet {
    my ($self) = @_;
    say "Hi, I'm " . $self->name;
}
# Moo automatically generates constructor, accessors, type checking

Key OOP Concepts Summary

ConceptTraditional PerlMoose / Moo
Classpackage Foo;package Foo; use Moose;
Constructorsub new { bless {}, $class }Auto-generated
AttributesHash keys in $selfhas 'attr' => (is=>'rw');
Inheritanceuse parent 'Base';extends 'Base';
Mixins/RolesMultiple inheritancewith 'Role::Name';
Method overridesub foo { ... SUPER::foo ... }around 'foo' => sub { ... };
Type checkManual validationisa => 'Int' or Types::Standard
Destructionsub DESTROY { ... }DEMOLISH
19 Modules & CPAN

use vs require

userequire
When executedCompile time (early)Runtime (when line reached)
ImportCalls import() automaticallyDoes not call import()
Version checkuse 5.036; worksOnly for modules
Typical useAlmost alwaysConditional loading
using_modules.plperl
use List::Util qw(sum max min first);   # import specific subs
use Scalar::Util ();                     # load but don't import
Scalar::Util::blessed($obj);             # call fully-qualified
use POSIX qw(floor ceil strftime);

Writing a Module

MyUtil.pmperl
package MyUtil;
use strict; use warnings;
our $VERSION = '1.00';

use Exporter 'import';
our @EXPORT_OK   = qw(util_one util_two);  # opt-in
our @EXPORT      = qw();                    # auto-export (avoid unless simple)
our %EXPORT_TAGS = (all => [@EXPORT_OK]);

sub util_one { ... }
sub util_two { ... }

1;    # MODULE MUST RETURN TRUE — never forget this!

Essential CPAN Modules by Category

ModuleCategoryPurpose
Moose / MooOOPPowerful, declarative object system
DBI + DBD::*DatabaseUniversal database interface
MojoliciousWebFull-stack web framework (no deps)
Dancer2WebLightweight sinatra-style web framework
LWP::UserAgentHTTPHTTP client (make web requests)
HTTP::TinyHTTPLightweight HTTP client (core module)
JSON / JSON::XSDataJSON encode/decode
YAML::PPDataYAML parse/generate
Text::CSV_XSDataRobust CSV parsing
DateTimeTimeComprehensive date/time manipulation
Path::TinyFilesElegant file/path operations
Getopt::LongCLICommand-line option parsing
Template (TT)TemplatesPowerful template engine
Try::TinyErrorsSimple, correct try/catch/finally
Test::MoreTestingStandard testing harness
CarpErrorsBetter error context (core)
StorableSerializeDeep copy, serialization (core)
Data::DumperDebugPretty-print any data structure (core)
installingshell
cpanm Module::Name           # cpanminus — recommended installer
cpan  Module::Name           # built-in CPAN shell
apt install libfoo-perl      # system package manager (Debian/Ubuntu)
brew install cpanminus       # macOS via Homebrew
20 Special Variables

Most Commonly Used

VariableEnglish nameMeaning
$_$ARGDefault variable for loops, print, match, chomp, etc.
@_Subroutine argument list — always unpack at top of sub
$!$OS_ERRORSystem error from last failed OS call (as string or number)
$@$EVAL_ERRORException caught by last eval block
$?$CHILD_ERRORExit status of last system() or backtick command
$0$PROGRAM_NAMEName of the running script
@ARGVCommand-line arguments
%ENVEnvironment variables (read/write)
$/$INPUT_RECORD_SEPInput record separator (default: \n); set to undef to slurp
$\$OUTPUT_RECORD_SEPAppended to every print statement
$,$OUTPUT_FIELD_SEPSeparator between print arguments
$"$LIST_SEPSeparator used when array interpolated in string (default: space)
$.$INPUT_LINE_NUMBERCurrent line number of last filehandle read
$;$SUBSCRIPT_SEPMulti-key hash subscript separator (rare)
$&$MATCHEntire string matched by last regex
$1..$9Captured groups from last successful regex match
%+Named captures from last regex: $+{name}
$^W$WARNINGTrue if warnings enabled (prefer use warnings)
$^O$OSNAMEOperating system name: "linux", "darwin", "MSWin32"
$^T$BASETIMETime (epoch) when program started
NoteUse use English; to get readable aliases for punctuation variables. Example: $OS_ERROR instead of $!. However, this has a small performance cost for regex-related variables, so some skip it in tight loops.
21 Command-Line Perl

Flags

FlagEffect
-e 'code'Execute code string directly (no .pl file needed)
-nWrap code in while (<>) { }; reads lines, sets $_; no automatic print
-pLike -n but prints $_ after each iteration (like sed)
-i[ext]Edit files in-place; optional extension creates backup: -i.bak
-aAuto-split $_ on whitespace into @F (use with -n/-p)
-F/pat/Set split pattern for -a (instead of whitespace)
-lAuto-chomp input lines; append $/ to print output
-0[oct]Set $/ to given octal value (0 = null, 777 = slurp whole file)
-cCheck syntax only; don't execute
-wEnable warnings (use use warnings in scripts instead)
-dRun under debugger
-de 42Start interactive debugger (Perl REPL)
-M ModuleLoad a module before executing: perl -MList::Util=sum -e 'say sum(1..10)'

One-Liner Recipes

one_linersshell
# Print lines matching a pattern
perl -ne 'print if /ERROR/'  server.log

# Replace text in-place across multiple files
perl -pi.bak -e 's/\bfoo\b/bar/g'  *.txt

# Print only lines 15-17
perl -ne 'print if $. >= 15; last if $. >= 17'  file.txt

# Sum a column of numbers (2nd field)
perl -ane '$sum += $F[1]; END { say $sum }'  data.txt

# Remove duplicate lines (preserving order)
perl -ne 'print unless $seen{$_}++'  file.txt

# Count occurrences of each word
perl -ne 'for (split){ $c{$_}++ } END{ say "$_ $c{$_}" for sort keys %c }'  file.txt

# Reverse each line
perl -lpe '$_ = reverse'  file.txt

# Rename files: strip .txt from all *.txt.bak files
perl -e 'rename $_, s/\.txt//r for glob "*.txt.bak"'

# Pretty-print JSON (requires JSON::PP)
perl -MJSON::PP -e 'print JSON::PP->new->pretty->encode(decode_json(do{local $/;<STDIN>}))'

# Find and print palindromes in a word list
perl -lne 'print if lc eq reverse lc'  /usr/share/dict/words
22 Advanced Topics

Operator Overloading

overload.plperl
package Vector;
use overload
    '+'  => \&add,
    '""' => \&stringify;   # overload stringification

sub new       { bless { x => $_[1], y => $_[2] }, $_[0] }
sub add       { Vector->new($_[0]{x}+$_[1]{x}, $_[0]{y}+$_[1]{y}) }
sub stringify { "($_[0]{x}, $_[0]{y})" }

my $v = Vector->new(1,2) + Vector->new(3,4);
say $v;    # "(4, 6)"

Formats & Context Summary

ContextHow triggeredEffect
Scalarmy $n = @arrArray returns element count; localtime returns formatted string
Listmy @copy = @arr, my ($a,$b) = func()Array/hash expands to elements; functions return full list
Booleanif (@arr)Undef/0/"0"/"" are false; everything else is true
Voidfunc(); (ignoring return)Function may optimize by not building return value
Numeric$s + 0String converted to number; non-numeric string → 0 + warning
String$n . ""Number converted to string representation

Advanced Features Overview

Weak references (Scalar::Util::weaken) inside-out objects AUTOLOAD / UNIVERSAL tie (overload variables) BEGIN / END / INIT blocks CHECK / UNITCHECK Symbol table manipulation typeglobs (*foo) threads (ithreads) fork() + wait() Inline::C XS extensions Attribute::Handlers Given/when (experimental)
23 Best Practices

Code Style (from Perl Best Practices)

CategoryRule
SafetyAlways use strict; use warnings; — no exceptions
SafetyAlways unpack @_ explicitly at the start of every subroutine
SafetyAlways use 3-argument open() and check the return value
SafetyNever use bareword filehandles
SafetyAvoid symbolic references entirely
ClarityUse my for every variable; minimize scope
ClarityUse //= not ||= when 0 or "" are valid values
ClarityPrefer named variables over $_ when the name aids clarity
ClarityUse elsif, not cascaded if checks on the same variable
NamingVariables: $my_variable (snake_case for scalars/arrays/hashes)
NamingConstants: MAX_RETRIES (ALL_CAPS)
NamingPackages/Classes: MyApp::Parser (CamelCase)
NamingPrivate subs: prefix with underscore _helper()
Layout4-space indentation; 78-column line limit
LayoutTrailing comma on last element of multiline list
LayoutAlign corresponding items vertically
SubroutinesUse named parameters (hash) for 3+ arguments
SubroutinesAlways use explicit return
Error handlingThrow exceptions (die) instead of returning error flags
ModulesExport on request (@EXPORT_OK), not automatically
OOPDon't use indirect object syntax: new Foo() → use Foo->new()
PerformanceDon't optimize without profiling (use Devel::NYTProf)
TestingWrite tests first (Test::More, Test::Exception)

Modern Perl Style

modern.plperl
# Modern preamble (Perl 5.36+)
use v5.36;       # enables strict, warnings, say, state, and more
use utf8;
use feature 'signatures';   # named params in subs
no warnings 'experimental::signatures';

# Named sub parameters (Perl 5.20+ with signatures)
sub greet ($name, $greeting = "Hello") {
    say "$greeting, $name!";
}

# Try::Tiny for clean exception handling
use Try::Tiny;
try {
    risky_op();
} catch {
    warn "Error: $_";
} finally {
    cleanup();
};
24 Learning Roadmap

Stage 1 — Foundations (Weeks 1-2)

Program structure + pragmas Scalars, strings, numbers Arrays and hashes if/elsif/unless + loops Subroutines Reading/writing files

Project: Write a script that reads a CSV, processes it, and writes a summary report.

Stage 2 — Intermediate (Weeks 3-5)

References and nested structures Regular expressions (match, substitute) map / grep / sort Error handling (eval/die) Modules and CPAN Scope and closures

Project: Build a log-file analyzer that parses Apache logs, categorizes errors, and generates an HTML report using regex and data structures.

Stage 3 — Object-Oriented (Weeks 6-8)

Basic OOP (package/bless) Inheritance and SUPER Moose or Moo Writing and distributing modules Test::More and testing patterns

Project: Build a small ORM layer that wraps DBI with objects representing database rows.

Stage 4 — Advanced Mastery (Ongoing)

Advanced regex (lookahead, lookbehind, named captures) Dispatch tables and metaprogramming Operator overloading Performance profiling Algorithms and data structures XS and Inline::C

Canonical Books (Your Bookshelf)

BookLevelBest for
Learning Perl (Llama book)BeginnerFirst Perl book; covers all fundamentals clearly
Intermediate Perl (Alpaca book)IntermediateReferences, OOP, modules, testing
Programming Perl (Camel book)ReferenceThe definitive comprehensive reference
Modern PerlIntermediate+Current idioms, Moose, CPAN best practices
Perl Best PracticesIntermediateCode quality, style, naming conventions
Mastering Algorithms with PerlAdvancedCS algorithms implemented in Perl

Key Online Resources

ResourceURL
Official documentationperldoc.perl.org
CPAN module searchmetacpan.org
Modern Perl book (free)modernperlbooks.com
PerlMonks forumperlmonks.org
Perl Weekly newsletterperlweekly.com
Learn Perl in ~2hlearn.perl.org