Open-source libraries used

1 libraries are bundled into this tool's code.

Perl Cheatsheet โ€” Concise Reference

A cheatsheet for Perl 5.38 syntax, regular expressions, references, and the most common built-in functions โ€” covering ~80% of daily use cases.

Pl

Perl Perl 5.38

perl 5 (interpreter) ยท Multi-paradigm (procedural ยท OO ยท functional) ยท Dynamic

Recommended Learning Path

Start with running `perl hello.pl` and `use strict`/`use warnings` โ†’ master scalars, arrays, hashes, and control flow โ†’ dive into subroutines and context โ†’ build complex data structures with references โ†’ process text with regex โ†’ understand `eval` error handling and `bless`-based OO โ†’ then learn threads, networks, time, and CPAN modules as needed. The FAQ section is great for revisiting and avoiding pitfalls.

1.Hello World and Build Environment

Run Perl scripts, use strict/warnings, and command-line tools.

Minimal Program

Save as hello.pl, run with perl hello.pl. use strict and use warnings are standard.

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
say "Hello, world!";
# Run:
# perl hello.pl
# First line shebang points to the interpreter
# say adds a newline, print does not

Run and Interpret

perl directly interprets and executes scripts; -c only does syntax check, -e executes single-line code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Run the script directly:
# perl hello.pl
# Syntax check (no execution):
# perl -c hello.pl
# Single-line code from the command line:
# perl -e 'print "hi\n"'
# Enable warnings:
# perl -w hello.pl
# Print the load path:
# perl -V
# Line-by-line processing (-n/-p):
# perl -ne 'print if /error/' log.txt
# Locate the interpreter:
# which perl

Shebang and Arguments

#!/usr/bin/perl declares the interpreter; command-line arguments are in @ARGV, $0 is the script name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
# Command-line arguments are stored in @ARGV:
my ($name) = @ARGV;
say "Hello, $name" if defined $name;
# Run:
# perl greet.pl Rex
# Iterate over all arguments:
for my $arg (@ARGV) {
say "Argument: $arg";
}
# $0 is the script name:
say "Script: $0";
# Argument count:
my $count = scalar @ARGV;

Output and Formatting

print does not add newline, say adds newline automatically; printf formats, sprintf returns string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use strict;
use warnings;
use feature 'say';
print "hello\n"; # manual newline
say "world"; # automatic newline
# Formatted output:
printf "Name: %s, Age: %d\n", "Rex", 5;
# Return a string:
my $msg = sprintf("%d + %d = %d", 1, 2, 3);
say $msg;
# Output to a handle:
print STDERR "Error!\n";
# Common formatters:
# %s string, %d integer, %f float, %x hex

Reading Input

<STDIN> reads one line (including newline); chomp removes trailing newline; EOF returns undef.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use strict;
use warnings;
use feature 'say';
# Read one line (includes newline):
my $line = <STDIN>;
chomp $line; # remove trailing newline
say "Input: $line";
# Read one integer line:
my $num = <STDIN>;
chomp $num;
$num += 0; # coerce to number
# Read multiple lines until EOF:
while (my $l = <STDIN>) {
chomp $l;
last if $l eq 'quit';
say "Read: $l";
}
# <STDIN> returns undef at EOF

Quote Operators

q() single-quote semantics, qq() double-quote semantics, qw() word list, qx() command substitution.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use strict;
use warnings;
use feature 'say';
my $name = "Rex";
# q() does NOT interpolate:
my $a = q(literal $name);
# qq() DOES interpolate:
my $b = qq(Hello, $name);
# qw() splits on whitespace into a word list:
my @words = qw(alpha beta gamma);
# qx() executes a command and captures output:
my $pwd = qx(pwd);
chomp $pwd;
say "Dir: $pwd";
# Advantage: q-family operators accept any delimiter pair:
# q{...} qq(...) qw!...!
# Avoids escaping quotes

use strict/warnings

use strict forces variable declarations and disallows barewords; use warnings enables runtime warnings. Standard in modern Perl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use strict; # flags typos / barewords
use warnings; # runtime warnings
# The three faces of strict:
# strict 'vars' variables must be declared
# strict 'refs' prohibits symbolic references
# strict 'subs' prohibits bareword function names
# Undeclared variable โ€” compile error:
# $foo = 1;
# Reports "Global symbol requires explicit package name"
# Declare properly:
my $bar = 1;
say $bar;
# Production code should always include these two lines

perldoc Documentation

perldoc comes with Perl; look up functions, modules, built-in variables, and language tutorials.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Look up a built-in function:
# perldoc -f map
# Look up module docs:
# perldoc JSON
# Beginner tutorial:
# perldoc perlintro
# Built-in special variables:
# perldoc perlvar
# Operator precedence:
# perldoc perlop
# Full list of built-in functions:
# perldoc perlfunc
# Where a module is installed:
# perldoc -l JSON
# Quick syntax reference:
# perldoc perlrequick

2.Variables and Constants

Scalar/array/hash declarations, my/our/local/state, and constant definitions.

Variable Declarations

Three sigils identify three containers: $ scalar, @ array, % hash. my declares lexical variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use strict;
use warnings;
# Three sigils:
my $scalar = 42;
my @array = (1, 2, 3);
my %hash = (name => 'Rex', age => 5);
# Declared but not assigned:
my $later;
my (@a, %h);
# Bulk assignment:
my ($x, $y, $z) = (1, 2, 3);
# List destructuring:
my @nums = (1, 2, 3, 4);
my ($first, @rest) = @nums;
# $first=1, @rest=(2,3,4)

Scalar Type

A scalar holds one value: number, string, or reference. Strings and numbers convert automatically.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use strict;
use warnings;
my $num = 3.14; # number
my $str = "hello"; # string
my $bool = 1; # false values: 0 '' undef '0'
my $undef; # unassigned is undef
# Automatic conversion (by context):
my $sum = "3" + 4; # 7 (numeric context)
my $s = 3 . 4; # "34" (string concatenation)
# Numeric coercion prefix:
my $n = "12abc" + 0; # 12
# Length:
my $len = length $str; # 5

Array Operations

Arrays are lists of elements. push/pop operate on the end, shift/unshift on the beginning; indices start at 0.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use strict;
use warnings;
use feature 'say';
my @nums = (1, 2, 3);
$nums[0] = 10; # modify an element
say $nums[0];
# Stack operations:
push @nums, 4; # append to end
my $last = pop @nums; # pop from end
unshift @nums, 0; # prepend at start
my $first = shift @nums; # pop from start
# Length:
my $count = scalar @nums;
# Last element:
my $end = $nums[-1];
# Slice:
my @sub = @nums[0, 1];

Hash Operations

Hashes are key-value pairs with string keys. Iterate with keys/values/each, manage with exists/delete.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
use feature 'say';
my %ages = (alice => 30, bob => 25);
# Read / modify:
say $ages{alice};
$ages{carol} = 28;
# Delete and check:
delete $ages{bob};
exists $ages{alice}; # does the key exist?
# Iteration:
for my $name (keys %ages) {
say "$name: $ages{$name}";
}
# Get key-value pairs at the same time:
while (my ($k, $v) = each %ages) {
say "$k=$v";
}
# Note: each consumes the iterator; do not add/delete while iterating

Constant Definitions

use constant defines compile-time constants with no sigil, called like subroutines.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use strict;
use warnings;
# use constant defines a constant:
use constant PI => 3.14159;
use constant MAX_RETRIES => 3;
# Constants are called like subroutines (no sigil):
my $area = PI * 2 * 2;
# Constant hash / array:
use constant SETTINGS => { debug => 1 };
# Read-only semantics:
# Provided by the Readonly and Const::Fast modules
# Constants cannot be interpolated directly:
# my $s = "PI = PI"; # not substituted
# Interpolate via concatenation or a temporary variable

Scope

my lexical variables are visible within the block; our declares package (global) variables; local temporarily overrides globals; state provides static lexical variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'state';
# my โ€” block scope:
{
my $temp = 10;
say $temp; # visible inside the block
}
# $temp does not exist outside the block
# our โ€” package variable (global):
our $VERSION = '1.0';
# local โ€” temporarily override a special variable:
{
local $/; # input separator temporarily undef
my $all = <$fh>; # read the whole file at once
}
# $/ is restored when the block ends
# state โ€” retains value across calls:
sub counter {
state $n = 0;
return ++$n;
}
say counter(); # 1
say counter(); # 2

Default Variables

$_ is the default input variable, @_ is the argument list; common special variables include $! for system errors and $0 for the script name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
# $_ default variable:
for (1..3) {
say $_; # set as default by the loop
}
# Many functions operate on $_ by default:
for (@ARGV) {
chomp;
print;
}
# @_ argument list:
sub sum {
my $t = 0;
$t += $_ for @_;
return $t;
}
# Common special variables:
# $0 script name, $! last system error
# $/ input separator, $| autoflush
# $$ process id, $ARGV current file name

String Interpolation

In double quotes, $scalar and @array are interpolated; single quotes output literally; curly braces delimit variable names.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
use feature 'say';
my $name = "Rex";
my @list = (1, 2);
# Double-quote interpolation:
my $a = "Hello, $name"; # scalar
my $b = "Items: @list"; # array (space-separated)
# Escape a literal $:
my $c = "Escaped: \$name";
# Single quotes do NOT interpolate:
my $d = 'Hello, $name';
# Braces delimit variable names:
my $e = "${name}s house";
# Index / dereference interpolation:
my $h = [1, 2, 3];
my $f = "Second: $h->[1]";
# Escape: \n newline, \t tab, \\ backslash

3.Data Types

Scalars, undef, truthiness, lists and hashes, file handles, and typeglobs.

Scalar Type

Scalar is the only basic type; it can hold integers, floats, strings, or references. No explicit type declaration needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use strict;
use warnings;
use feature 'say';
my $n = 42; # integer
my $f = 3.14; # float
my $s = "text"; # string
my $r = \@ARGV; # reference
my $u; # undef
# Char <-> codepoint:
my $ch = chr(65); # "A"
my $ord = ord('A'); # 65
# Numeric bases:
my $hex = 0xFF; # 255
my $oct = 0b1010; # 10
# Large-number precision:
# use bigint / Math::BigInt
# The type changes automatically with assignment

Number/String Conversion

Automatic conversion based on context: + for numeric, . for concatenation. Explicit conversion uses +0 and ."".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use strict;
use warnings;
use feature 'say';
# Automatic conversion:
my $sum = "3" + "4"; # 7 (numeric context)
my $cat = 3 . 4; # "34" (string)
# Explicit conversion:
my $n = "42" + 0; # string to number
my $s = 42 . ""; # number to string
# Numeric prefix rules:
my $v = "12px" + 0; # 12
# Non-numeric prefix yields 0:
my $z = "abc" + 0; # 0
# Formatted output:
my $out = sprintf "%.2f", 3.14159; # 3.14
# Number vs string comparison operators differ:
# == != < > numeric; eq ne lt gt string

undef and defined

undef means undefined; in numeric context it is 0, in string context empty string. Use defined to test.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use strict;
use warnings;
use feature 'say';
my $x; # unassigned means undef
say "Undefined" unless defined $x;
# undef's value by context:
my $y = $x + 1; # 1 (numeric: 0)
my $s = "Value: $x"; # Value: (string: empty)
# Test:
if (defined $x) {
say "Defined";
}
# Default-assign:
$x //= 5; # assign 5 if undef
say $x; # 5
# Explicit undef:
undef $x;
# // is the defined-or operator (5.10+)

Truthiness

Only undef, 0, "0", and empty string are false; everything else is true. Logical operators return operands.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use strict;
use warnings;
use feature 'say';
# False values: undef, 0, "0", ""
my @false = (undef, 0, "0", "");
# Everything else is true:
my @true = (1, "0.0", " ", [], {});
if ("0.0") { say "This is true" } # non-empty string
if ([]) { say "Array ref is true" }
# Logical operators return the last evaluated operand:
my $a = 0 || "fallback"; # "fallback"
my $b = 1 && "x"; # "x"
# Defined-or:
my $c = $undef // "default"; # "default"
# Use ? : to produce booleans:
my $ok = 5 > 3 ? 1 : 0;

Arrays and Lists

List literals (1,2,3), qw word lists, range 1..5. Context determines whether the whole list or individual elements are used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use strict;
use warnings;
my @list = (1, 2, 3);
my @empty = ();
# qw โ€” word list:
my @names = qw(alice bob carol);
# Ranges:
my @digits = (1..5); # 1 2 3 4 5
my @chars = ('a'..'e'); # a b c d e
# List assignment:
my ($a, $b, $c) = (1, 2, 3);
# Flatten on nesting:
my @flat = (1, (2, 3), 4); # 1 2 3 4
# Context difference:
my @x = (1, 2); # list, keeps everything
my $y = (1, 2); # scalar, takes the last 2
# Scalar context gives array length:
my $len = @x; # 2

Hash Key-Value Pairs

The fat arrow => auto-quotes the left side; keys and values appear in pairs; keys are strings or integers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
# Fat arrow auto-quotes:
my %h = (name => 'Rex', age => 5);
# Equivalent form:
my %h2 = ('name', 'Rex', 'age', 5);
# Numeric keys:
my %num = (1 => 'one', 2 => 'two');
# Empty hash:
my %empty = ();
# Hash to list:
my @kv = %h; # ('name','Rex','age',5)
# Mixed style:
my %mixed = (x => 1, 'y', 2);
# Key access uses braces:
my $name = $h{name};
# Brace key may be an expression:
my $key = 'age';
my $age = $h{$key};

File Handles

STDIN/STDOUT/STDERR are built-in handles; open lexical filehandles to read/write files; the three-argument form is safest.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
# Built-in handles:
print STDOUT "stdout\n";
print STDERR "stderr\n";
my $line = <STDIN>; # read input
# Three-argument open:
open my $in, '<', 'data.txt' or die $!;
my $first = <$in>;
close $in;
# Write handle:
open my $out, '>', 'out.txt' or die $!;
print $out "hello\n";
close $out;
# Detect whether a handle is a TTY:
my $tty = -t STDOUT;
# A handle is a scalar โ€” pass it as an argument:
sub dump_all {
my $fh = shift;
print $fh @_;
}

Typeglob

The * prefix denotes a symbol table entry; used to alias scalars/arrays/subroutines. Less common in modern code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use strict;
use warnings;
my $scalar = 1;
my @array = (1, 2);
# typeglob aliases:
*alias = \$scalar; # scalar alias
*array_alias = \@array;
# Alias points to the same data:
$alias = 99;
say $scalar; # 99
# The symbol table entry contains all slots of the same name:
# *name includes $name @name %name &name
# Common uses:
# 1. Old-style filehandle: *FH = *STDOUT;
# 2. Function alias: *f = \&original;
# Modern code prefers references:
my $r = \$scalar;
${$r} = 100;

4.References and Dereferencing

Perl uses references instead of pointers: backslash to take address, dereferencing, arrow operator, and nested structures.

Reference Basics

Backslash \ takes a reference to a variable, producing a scalar of reference type; the ref function returns the type name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
use feature 'say';
my $name = "Rex";
my @arr = (1, 2, 3);
my %h = (a => 1);
my $sref = \$name; # scalar reference
my $aref = \@arr; # array reference
my $href = \%h; # hash reference
# ref returns the type name:
say ref $sref; # SCALAR
say ref $aref; # ARRAY
say ref $href; # HASH
# Non-reference returns empty string:
my $plain = 5;
say ref $plain; # ""
# A reference is a scalar โ€” store in arrays/hashes:
my @refs = ($aref, $href);

Dereferencing

@$ref, %$ref, $$ref restore a reference to its original type; the brace form ${$ref} is more visible.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
my @arr = (10, 20, 30);
my %h = (a => 1, b => 2);
my $aref = \@arr;
my $href = \%h;
# Array dereference:
say $aref->[0]; # 10 (preferred)
say ${$aref}[0]; # 10
# Hash dereference:
say $href->{a}; # 1
say ${$href}{a}; # 1
# Scalar dereference:
my $val = 99;
my $sref = \$val;
say $$sref; # 99
say ${$sref}; # 99
# Unwrap the whole thing:
my @copy = @$aref; # dereference to a list
my %copy = %$href;
# Get length / keys:
my $n = scalar @$aref;

Arrow Operator

-> dereferences nested structures: $ref->[0] and $ref->{key}, chained layer by layer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
my $ref = [10, 20, 30];
say $ref->[0]; # 10
my $href = {name => 'Rex', age => 5};
say $href->{name}; # Rex
# Multi-level nesting:
my $matrix = [[1, 2], [3, 4]];
say $matrix->[1][0]; # 3
my $users = [
{name => 'alice', age => 30},
{name => 'bob', age => 25},
];
say $users->[1]{name}; # bob
# Calling a function reference:
my $fn = sub { "hi" };
say $fn->(); # hi
# Equivalent brace form:
say ${$users->[0]}{name}; # alice

Anonymous References

[] anonymous array, {} anonymous hash, sub {} anonymous function; construct directly without named variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
use feature 'say';
# Anonymous array:
my $aref = [1, 2, 3];
my $empty = [];
# Anonymous hash:
my $href = {name => 'Rex'};
# Anonymous function:
my $fn = sub { "hello" };
# Build nested structures directly:
my $config = {
name => 'app',
hosts => ['a.com', 'b.com'],
};
say $config->{hosts}[0]; # a.com
# Round parens = list, square brackets = reference:
my @list = (1, 2, 3); # list
my $ref = [1, 2, 3]; # reference
# Anonymous references skip the named variable step

Nested Structures

Composite structures like array of hashes, hash of arrays; dereference layer by layer when iterating.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use feature 'say';
# Array of arrays (AoA):
my $grid = [[1, 2], [3, 4]];
say $grid->[0][1]; # 2
# Hash of hashes (HoH):
my $people = {
alice => {age => 30, city => 'NY'},
bob => {age => 25, city => 'LA'},
};
say $people->{bob}{city}; # LA
# Hash of arrays (HoA):
my $grades = {
math => [90, 85],
eng => [88],
};
say $grades->{math}[0]; # 90
# Iterate over nested structures:
for my $name (keys %$people) {
say "$name: $people->{$name}{age}";
}
# Use Data::Dumper to inspect deep structures

Reference Parameters

Passing references avoids copying large arrays and lets you modify caller's data in place; returning references is more efficient.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
my @big = (1..1000);
# Pass a reference for sum (no array copy):
sub total {
my $arr = shift;
my $sum = 0;
$sum += $_ for @$arr;
return $sum;
}
say total(\@big);
# Modify the caller's data in place:
sub bump {
my $arr = shift;
$_++ for @$arr;
}
bump(\@big);
say $big[0]; # 2 (was modified)
# Pass multiple references:
sub merge {
my ($a, $b) = @_;
return [@$a, @$b]; # return an anonymous reference
}
my $m = merge([1, 2], [3, 4]);
say $m->[2]; # 3

bless and Objects

bless tags a reference with a class name to make it an object; ref then returns the class name. An object is essentially a reference.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
use feature 'say';
package Animal;
sub new {
my $class = shift;
my %args = @_;
return bless { %args }, $class;
}
sub speak {
my $self = shift;
return "$self->{name} makes a sound";
}
package main;
my $cat = Animal->new(name => 'meow');
say $cat->speak; # meow makes a sound
# An object is a hash reference tagged with a class:
say ref $cat; # Animal
say $cat->{name}; # meow
# For full OO, see the oop section

Reference Comparison

Compare references by address with ==; ref returns the type; reference counting manages lifetime.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
my @a = (1, 2);
my @b = (1, 2);
my $r1 = \@a;
my $r2 = \@a; # same target
my $r3 = \@b; # different target
# Compare references by address with ==:
say $r1 == $r2; # 1
say $r1 == $r3; # 0
# eq compares stringified form:
say $r1 eq $r2; # 1
# ref reports the type:
say ref $r1; # ARRAY
say ref \5; # SCALAR
say ref undef; # ""
# Check type before dereferencing:
if (ref $href eq 'HASH') {
say $href->{name};
}
# See the mem section for reference counting

5.Control Flow

if/unless, postfix form, for/foreach/while, and next/last/redo.

if/elsif/else

if tests truthiness; elsif chains; unless is the reverse. Parentheses around conditions are not required.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
use feature 'say';
my $score = 85;
if ($score >= 90) {
say "Excellent";
} elsif ($score >= 60) {
say "Pass";
} else {
say "Fail";
}
# Falsy check (0 / empty / undef are false):
my $name = "Rex";
if ($name) { say "Has a name" }
# unless = if not:
my $debug = 0;
unless ($debug) { say "Debug off" }
# Braces cannot be omitted:
# if ($x) say 1; syntax error
# Any expression may serve as the condition:
if (-f 'data.txt') { say "Is a file" }

unless and until

unless is the negation of if, until is the negation of while; do-until executes at least once.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use feature 'say';
my $debug = 0;
# unless = if not:
unless ($debug) {
say "Debug output disabled";
}
# until = while not:
my $n = 0;
until ($n >= 3) {
say "n=$n";
$n++;
}
# Postfix form:
say "Default" unless defined $config;
# do-until executes at least once:
do {
$n--;
} until ($n == 0);
# Best for inverted conditions โ€” improves readability
# Complex conditions still warrant if/while

Postfix Control

if/unless/for/while can follow a single statement โ€” Perl idiomatic style. Only modifies one statement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
use feature 'say';
my $n = 120;
# Postfix modifier on a statement:
say "big" if $n > 100;
die "timeout" unless $done;
for my $i (1..5) {
say "Item $i";
}
# Postfix for:
print "$_ " for 1..3;
my $total = 0;
$total += $_ for @nums;
# Equivalent prefix style:
# if ($n > 100) { say "big" }
# for (@nums) { $total += $_ }
# Modifies only ONE statement:
# say 1; say 2 if $c; โ€” only say 2 is governed by if
# Multiple statements need a normal block

for/foreach Loop

for can be written C-style or iterate lists; foreach is an alias. Without a variable, $_ is used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
# C-style:
for (my $i = 0; $i < 5; $i++) {
say $i;
}
# Iterate over a list:
for my $item (1..3) {
say $item;
}
# Iterate over an array:
my @names = qw(alice bob);
for my $name (@names) {
say $name;
}
# Omit the variable โ€” uses $_:
for (1..3) {
say "Current: $_";
}
# foreach is fully equivalent:
foreach my $x (@names) {
say $x;
}
# Empty list โ€” loop body never runs

while Loop

while loops while condition is true; reading files line by line with while is most idiomatic; do-while executes once first.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use strict;
use warnings;
use feature 'say';
# Read a file line by line:
open my $fh, '<', 'data.txt' or die $!;
while (my $line = <$fh>) {
chomp $line;
say $line;
}
# Returns undef at EOF, terminating the loop
# Infinite loop:
my $done = 0;
while (1) {
last if $done;
$done = 1;
}
# Execute at least once:
my $x = 10;
do {
say $x;
$x--;
} while ($x > 5);
# until โ€” negated:
my $i = 0;
until ($i > 3) {
say "i=$i";
$i++;
}

next/last/redo

next skips current iteration, last exits loop, redo repeats current iteration; labels control nested loops.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
use strict;
use warnings;
use feature 'say';
# next โ€” skip current iteration:
for my $i (1..10) {
next if $i % 2; # skip odd numbers
say $i;
}
# last โ€” exit the loop:
for my $i (1..10) {
last if $i > 5;
say $i;
}
# redo โ€” repeat current iteration:
my $sum = 0;
my $i = 0;
BLOCK: {
$i++;
$sum += $i;
redo BLOCK if $i < 3;
}
say $sum; # 6
# Labels control nested loops:
OUTER: for my $x (1..3) {
for my $y (1..3) {
next OUTER if $y == 2;
say "$x,$y";
}
}

Ternary and Short-Circuit

?: conditional expression; || and // provide defaults; logical operators return operands.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
my $n = -3;
# Ternary:
my $sign = $n >= 0 ? "non-negative" : "negative";
say $sign;
# || default value (replace if falsy):
my $name;
my $shown = $name || "Anonymous";
# // defined-or (replace only undef):
my $port;
$port //= 8080; # assign default only if undef
# Logical returns its operand:
my $a = 0 && die "won't run";
my $b = 1 || die "won't run";
# Nested ternaries hurt readability:
# my $g = $n>0 ? "pos" : $n<0 ? "neg" : "zero";
# Prefer splitting into if/elsif

goto and Labels

goto can jump to labels (limited form); prefer labelled last for multi-level exits. goto is rarely used in everyday code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use feature 'say';
# Restricted goto โ€” jump to a label:
my $i = 0;
START:
$i++;
goto START if $i < 3;
say "Done: $i";
# goto cannot jump INTO a block
# Prefer loop control:
my @items = (1, -1, 2);
for my $item (@items) {
last if $item < 0;
say $item;
}
# Multi-level exit โ€” label + last:
OUTER: for my $a (1..3) {
for my $b (1..3) {
last OUTER if $a * $b > 4;
say "$a x $b";
}
}
# Favor readability โ€” avoid goto jumping around

6.Functions and Subroutines

sub definitions, @_ arguments, return values, closures, prototypes, and signatures.

Function Definition

sub defines a named subroutine; the last expression is returned automatically. Use parentheses to pass arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use feature 'say';
# Define and call:
sub greet {
return "hello";
}
say greet(); # hello
# With arguments:
sub add {
my ($a, $b) = @_;
return $a + $b;
}
say add(2, 3); # 5
# The last expression returns automatically:
sub square { my $x = shift; $x * $x }
say square(4); # 16
# No return value:
sub log_msg {
say "Log entry";
}
# Subroutine names are case-sensitive
# Calling an undefined sub is a compile-time error

@_ and Arguments

Arguments are in @_; shift takes the first; @_ elements are aliases โ€” modifying in place propagates back.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
use strict;
use warnings;
use feature 'say';
# @_ is the argument list:
sub first_arg {
my $x = shift; # take the first one
return $x;
}
# Multiple arguments:
sub two {
my ($a, $b) = @_; # list assignment
return "$a-$b";
}
say two(1, 2); # 1-2
# Inside a sub, shift operates on @_ by default:
sub total {
my $sum = 0;
$sum += shift while @_;
return $sum;
}
say total(1, 2, 3); # 6
# @_ elements are aliases โ€” modifying them leaks out:
sub bump_first { $_[0]++ }
my $x = 1;
bump_first($x);
say $x; # 2
# Normally copy to lexical variables first

Return Values

Return scalar or list depending on context; wantarray detects caller context; return references to avoid copying.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
sub coords { return (1, 2, 3) }
# In list context โ€” returns them all:
my @all = coords(); # (1,2,3)
# In scalar context โ€” returns the last:
my $n = coords(); # 3
# Empty return:
sub nothing { return }
# Returning undef:
sub missing { return undef }
# Use wantarray to detect caller's context:
sub ctx {
return wantarray ? (1, 2) : 5;
}
my @l = ctx(); # (1,2)
my $s = ctx(); # 5
# Return a reference to avoid copying large structures:
sub make_list {
return [1, 2, 3]; # anonymous array reference
}
my $r = make_list();

Anonymous Subroutine

sub {} creates a function reference, called via ->(); can be used as callback, stored in array or hash.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use strict;
use warnings;
use feature 'say';
# Anonymous subroutine:
my $square = sub {
my $x = shift;
return $x * $x;
};
say $square->(5); # 25
# Pass as an argument (callback):
sub apply {
my ($fn, $val) = @_;
return $fn->($val);
}
say apply(sub { $_[0] * 2 }, 21); # 42
# Store in an array:
my @handlers = (
sub { "one" },
sub { "two" },
);
say $handlers[1]->(); # two
# Store in a hash (dispatch table):
my %ops = (
add => sub { $_[0] + $_[1] },
mul => sub { $_[0] * $_[1] },
);
say $ops{add}->(2, 3); # 5
# A function reference is just another scalar reference

Closures

Closures capture lexical variables and remember state; each call to the factory creates an independent copy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
# Counter closure:
sub make_counter {
my $count = 0;
return sub {
$count++; # remembers state
return $count;
};
}
my $c = make_counter();
say $c->(); # 1
say $c->(); # 2
# Each has independent state:
my $a = make_counter();
my $b = make_counter();
$a->(); # 1
$b->(); # 1
# Factory function:
sub make_greeter {
my $prefix = shift;
return sub { "$prefix: $_[0]" };
}
my $hi = make_greeter("hi");
say $hi->("Rex"); # hi: Rex
# A closure captures the variable itself, not a copy

Prototypes

Prototypes constrain argument context at compile time, e.g. (\@) forces an array reference. Modern code prefers signatures.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
# Prototypes constrain arguments:
sub myshift (\@) {
my $arr = shift;
return shift @$arr;
}
my @nums = (1, 2, 3);
my $first = myshift(@nums); # coerced to a reference
say $first; # 1
# Common prototypes:
# ($) force scalar context
# (\@) force array reference
# (\%) force hash reference
# ($@) one scalar followed by an array
# Prototypes don't apply to method calls:
# $obj->meth(@args) ignores prototypes
# Prototypes can be misleading โ€” avoid unless writing built-in-style subs
# 5.36+ prefers signatures:
sub real($a, $b) { $a + $b }

Function Signatures

feature 'signatures' provides declarative parameters (stable in 5.36+); supports defaults and array parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'signatures';
no warnings 'experimental::signatures';
# Declarative arguments:
sub add($a, $b) {
return $a + $b;
}
say add(2, 3); # 5
# Default values:
sub greet($name = "world") {
return "hello, $name";
}
say greet(); # hello, world
# Multiple args + array slurpy:
sub config($name, @rest) {
return ($name, @rest);
}
# Arguments are lexicals โ€” no more @_:
sub swap($x, $y) {
return ($y, $x);
}
my ($u, $v) = swap(1, 2);
# Signatures read more clearly than shift
# No named arguments โ€” emulate with a hash

Function References

\&sub takes a function reference; call via ->() or &$ref(); used for callbacks, dispatch tables, and higher-order functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'say';
# Take a function reference:
sub twice { $_[0] * 2 }
my $fn = \&twice;
say $fn->(10); # 20
say &$fn(10); # 20
# Pass as an argument:
sub run {
my $code = shift;
$code->();
}
run(sub { say "Running" });
# Check whether it's a code reference:
my $cb = \&twice;
say ref $cb; # CODE
# Tail-call optimization (reuse the call frame):
sub loop {
return if $_[0] <= 0;
@_ = ($_[0] - 1);
goto &loop;
}
loop(100000);
# Deep recursion should prefer goto &sub

7.Strings

Quotes, interpolation, concatenation, formatting, substrings, and encoding.

String Literals

Single quotes literal, double quotes interpolate; q()/qq() are equivalent; adjacent strings concatenate automatically.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
use feature 'say';
my $name = "Rex";
# Single quotes do NOT interpolate:
my $a = 'literal $name';
# Double quotes DO interpolate:
my $b = "hi $name";
# q / qq operators (any delimiter):
my $c = q(literal $name);
my $d = qq(interpolated $name);
# Escape \ and ' inside single quotes:
my $e = 'it\'s ok';
# Adjacent literals concatenate automatically:
my $f = 'a' 'b' 'c'; # abc
# Newlines and tabs:
my $g = "line1\nline2\ttab";
# Watch quote nesting for escape rules:
my $h = "He said \"hello\"";

Interpolation Rules

In double quotes, $scalar and @array interpolate; curly braces delimit names; indices/dereferences also interpolate.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use feature 'say';
my $name = "Rex";
my @nums = (1, 2, 3);
# Scalar interpolation:
my $a = "Hello, $name";
# Array interpolation (space-separated):
my $b = "Nums: @nums"; # Nums: 1 2 3
# Braces delimit variable boundaries:
my $s = "${name}s house";
# Index interpolation:
my @h = qw(x y z);
my $c = "Second: $h[1]"; # y
# Dereference interpolation:
my $user = {name => 'Rex'};
my $d = "Name: $user->{name}";
# Escape:
my $e = "newline\ntab\t";
# \\ backslash, \$ literal dollar sign
# Variable names with non-word characters need braces

Concatenation and Repetition

. concatenates strings, x repeats, join joins arrays; length gives length, reverse reverses.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
# Dot concatenation:
my $full = "Hello" . " " . "World";
# Numbers in dot context become strings:
my $s = 1 . 2; # "12"
# Repetition operator:
my $line = "-" x 20; # 20 hyphen characters
# join โ€” concatenate an array:
my @parts = ('a', 'b', 'c');
my $csv = join ",", @parts; # a,b,c
# Length:
my $len = length "hello"; # 5
# Reverse:
my $rev = reverse "abc"; # cba
# String comparison:
"a" eq "a"; # equal
"a" lt "b"; # less than (lexicographic)
"abc" cmp "abd"; # comparison result
# Use .= to append in place:
my $buf = "x";
$buf .= "y"; # xy

Substrings and Search

substr gets/replaces substrings; index/rindex find positions (returns -1 if not found).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
my $s = "Hello, World";
# Take a substring:
my $sub = substr $s, 0, 5; # Hello
my $from = substr $s, 7; # World
my $last4 = substr $s, -5; # World (counted from end)
# Find a position:
my $pos = index $s, "World"; # 7
my $last = rindex $s, "o"; # 8 (search from the right)
# Returns -1 when not found:
index $s, "zzz"; # -1
# Replace a substring:
my $t = $s;
substr($t, 7, 5) = "Perl"; # Hello, Perl
# Split into lines with split:
my @words = split /,/, $s;
# For complex extraction, see the regex section

chomp and split

chomp removes trailing newline; split divides by delimiter into a list; join is the inverse.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
# chomp โ€” strip trailing newline:
my $line = "abc\n";
chomp $line; # "abc"
# chomp modifies in place and returns the count of removed chars
# split โ€” by delimiter:
my @f = split /,/, "a,b,c"; # a b c
my @w = split /\s+/, "a b"; # a b
# Omit delimiter โ€” split on whitespace:
my @t = split /\s+/, "a b c";
# Limit the number of fields:
my @two = split /,/, "a,b,c", 2; # a, "b,c"
# join is the inverse:
my $back = join "-", @f; # a-b-c
# String to array of lines:
my @lines = split /\n/, $text;
# For CSV parsing use Text::CSV

printf/sprintf

printf outputs directly, sprintf returns a string; %d/%s/%f formatters with zero-padding and alignment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use strict;
use warnings;
use feature 'say';
# printf โ€” direct output:
printf "%s is %d this year\n", "Rex", 5;
# sprintf โ€” returns a string:
my $s = sprintf "%05d", 42; # 00042
# Common specifiers:
# %s string %d integer %f float
# %x hex %o octal %e scientific
# %.2f two decimals %5d right-align with spaces
# %-5d left-align %05d zero-pad
my $money = sprintf "%.2f", 3.14159; # 3.14
my $hex = sprintf "%x", 255; # ff
# Dynamic width:
my $w = 10;
my $padded = sprintf "%${w}s", "hi";
# Output to a handle:
printf STDOUT "Value: %d\n", 42;
# Arguments are matched in order; multi-arg supported

Case and Trimming

uc/lc/ucfirst change case; use substitution regex to strip whitespace; tr/// is character translation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
my $s = "Hello World";
my $upper = uc $s; # HELLO WORLD
my $lower = lc $s; # hello world
my $cap = ucfirst $s; # Hello World
my $w = lcfirst "Hello"; # hello
# Trim leading / trailing whitespace (regex):
my $t = " padded ";
$t =~ s/^\s+//; # strip leading
$t =~ s/\s+$//; # strip trailing
# Prefix / suffix match:
$s =~ /^Hello/;
$s =~ /World$/;
# Character translation:
my $u = "abc";
$u =~ tr/a-z/A-Z/; # ABC
# Translation delete:
my $d = "a1b2c3";
$d =~ tr/0-9//d; # abc
# Char vs codepoint:
ord 'A'; # 65
chr 65; # "A"

Encoding and UTF-8

use utf8 marks source as UTF-8; encode/decode transcoding; read/write files with encoding layers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use utf8; # interpret source as UTF-8
use Encode;
my $chinese = "Chinese";
binmode STDOUT, ':utf8'; # output UTF-8
# Encode / decode:
my $bytes = encode('UTF-8', $chinese); # bytes
my $back = decode('UTF-8', $bytes); # characters
# Character count vs byte count:
my $text = "hello";
length $text; # 5 (characters)
length encode('UTF-8', $text); # 5 (bytes, ASCII here)
# Read / write with encoding layers:
open my $fh, '<:utf8', 'file.txt' or die $!;
open my $out, '>:utf8', 'out.txt' or die $!;
# JSON uses UTF-8:
use JSON::PP;
my $j = JSON::PP->new->utf8->encode({k => "Chinese"});

8.Collections and Data Structures

Array/hash operations, slices, map/grep, sorting, and List::Util.

Array Add/Remove/Modify

push/pop/shift/unshift for the four ends; splice for mid-array insert/delete; reverse/sort.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
my @nums = (1, 2, 3);
# Stack operations:
push @nums, 4; # append at end
my $top = pop @nums; # pop from end
unshift @nums, 0; # prepend at start
my $head = shift @nums; # pop from start
# Mid-array insert / remove:
splice @nums, 1, 0, 99; # insert 99 at index 1
splice @nums, 0, 1; # delete at index 0
# Reverse and sort:
my @rev = reverse @nums;
my @sorted = sort @nums;
# Merge:
my @all = (@nums, (4, 5));
# Append multiple at end:
push @nums, 6, 7;
# Length and last element:
my $len = scalar @nums;
my $end = $nums[-1];

Slices

@arr[...] takes an array slice, %hash{...} takes a hash slice; slices can be assigned as a whole.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
my @arr = qw(a b c d e);
# Array slice:
my @mid = @arr[1..3]; # b c d
my @sel = @arr[0, 2, 4]; # a c e
# Slices are assignable:
@arr[1, 2] = qw(X Y);
my %h = (alice => 30, bob => 25, carol => 28);
# Hash slice (fetch values for multiple keys):
my @ages = @h{qw(alice bob)}; # 30 25
# Hash slice assignment:
@h{qw(dan eve)} = (22, 35);
# Whole array / hash:
my @all = @arr;
# Slice on a reference:
my $r = \@arr;
my @sub = @$r[1..2];
# Slices in scalar context yield the count:
my $n = @arr[1..3]; # 3

List Operations

sum/min/max come from List::Util; grep for deduplication, range extraction, list destructuring.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
use List::Util qw(sum min max);
my @nums = (3, 1, 4, 1, 5);
my $sum = sum @nums; # 14
my $min = min @nums; # 1
my $max = max @nums; # 5
# Deduplicate (preserves first-seen order):
my %seen;
my @uniq = grep { !$seen{$_}++ } @nums;
# Merge:
my @a = (1, 2);
my @b = (3, 4);
my @ab = (@a, @b);
# List destructuring:
my ($x, $y) = @nums; # 3, 1
my ($head, @rest) = @nums;
# Top three after sort:
my @top3 = (sort { $b <=> $a } @nums)[0..2];
# Beware temporary array overhead when result is unused

map and grep

grep filters, map transforms; both iterate with $_. grep in scalar context returns the count.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
my @nums = (1, 2, 3, 4, 5);
# grep โ€” filter:
my @big = grep { $_ > 2 } @nums; # 3 4 5
my @even = grep { $_ % 2 == 0 } @nums; # 2 4
# map โ€” transform:
my @sq = map { $_ * $_ } @nums; # 1 4 9 16 25
my @labels = map { "Item $_ " } @nums;
# grep in scalar context returns the match count:
my $count = grep { $_ > 3 } @nums; # 2
# map builds a hash:
my %square = map { $_ => $_ * $_ } 1..5;
# Combine them:
my @big_sq = map { $_ * $_ }
grep { $_ > 2 } @nums; # 9 16 25
# grep/map don't mutate the original
# An unused mapped result is still computed

Hash Iteration

keys/values return key/value lists; each iterates key-value pairs; exists/delete manage keys.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
my %h = (a => 1, b => 2, c => 3);
# Key / value lists:
my @keys = keys %h;
my @values = values %h;
# Key-value iteration (each consumes an iterator):
while (my ($k, $v) = each %h) {
say "$k=$v";
}
# Each call yields the next pair
# Nested each is error-prone; stick to one level
# Existence and deletion:
exists $h{a}; # 1
delete $h{b};
# Clear:
%h = ();
# Merge:
my %m = (d => 4, %h);
# Invert key/value:
my %inv = reverse %m;
# Common counting pattern โ€” each + ++:
$count{$_}++ for @words;

Hash Idioms

Hashes for counting, deduplication, sets, caching, and grouping. Be careful with add/delete during iteration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use strict;
use warnings;
use feature 'say';
# Counting:
my @words = qw(a b a c b a);
my %count;
$count{$_}++ for @words; # a=>3 b=>2 c=>1
# Deduplicate:
my %seen;
my @uniq = grep { !$seen{$_}++ } @words;
# Set membership:
my %set = map { $_ => 1 } qw(a b c);
say "exists" if $set{a};
# Caching:
my %cache;
sub expensive {
my $key = shift;
return $cache{$key} if exists $cache{$key};
$cache{$key} = $key * $key;
return $cache{$key};
}
# Grouping (by first letter):
my %groups;
for my $word (@words) {
my $first = substr $word, 0, 1;
push @{ $groups{$first} }, $word;
}
# Array-valued hashes store arrayrefs โ€” dereference on read

Sorting

sort defaults to string order; use a custom comparison block with <=> for numeric, cmp for string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
my @nums = (5, 3, 8, 1);
# Default string sort:
my @s = sort @nums; # 1 3 5 8
# Numeric ascending / descending:
my @n = sort { $a <=> $b } @nums; # ascending
my @d = sort { $b <=> $a } @nums; # descending
# String compare:
my @w = sort { $a cmp $b } qw(banana apple);
# Sort by length:
my @len = sort { length $a <=> length $b }
@words;
# Stable sort (tie-breaker):
my @st = sort {
length $a <=> length $b || $a cmp $b
} @words;
# Iterate hash in sorted key order:
my %h = (b => 2, a => 1);
for my $k (sort keys %h) {
say "$k=$h{$k}";
}
# Inside the comparator, $a/$b are elements, not indices

List::Util

sum/sum0/min/max/first/reduce/any/all/shuffle. sum0 returns 0 for an empty list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'say';
use List::Util qw(
sum sum0 min max first reduce shuffle any all
);
my @nums = (3, 1, 4, 1, 5);
my $sum = sum @nums; # 14
my $sum0 = sum0 @nums; # empty list returns 0
my $min = min @nums; # 1
my $max = max @nums; # 5
# first โ€” first match:
my $f = first { $_ > 3 } @nums; # 4
# any / all:
my $has = any { $_ > 4 } @nums; # true
my $all = all { $_ > 0 } @nums; # true
# reduce โ€” fold:
my $prod = reduce { $a * $b } @nums;
# shuffle:
my @mix = shuffle @nums;
# Min / max across lists:
min @a, @b;
# Note: sum/min on an empty list return undef

9.Memory and Performance

Reference counting, circular references, weak references, autovivification, and performance benchmarking.

Reference Counting

Perl uses reference counting for automatic memory reclamation; no manual free needed. Released when the count reaches zero.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
# Automatic reference counting:
my $ref = [1, 2, 3]; # count is 1
my $copy = $ref; # count is 2
undef $copy; # count is 1
undef $ref; # count is 0 โ€” freed
# Lexical variables are freed when leaving scope:
{
my $tmp = "data";
} # $tmp is freed here
# No manual malloc/free:
# Create freely โ€” the engine handles cleanup
# Differs from C/Rust's explicit management
# Peak memory observation:
# undef large arrays right after use to free early
# Or stream instead of loading everything at once
# Circular references cannot be reclaimed by counting โ€” see next

Circular References

Mutual references keep the count non-zero and cause leaks; use weak references or manually break cycles.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
# Circular references cause leaks:
my $a = {};
my $b = {};
$a->{other} = $b; # a references b
$b->{other} = $a; # b references a
# Counts keep each other alive โ€” both blocks leak
# Self-references leak too:
my $node = {};
$node->{self} = $node;
# Fix 1: break the cycle manually:
$a->{other} = undef;
# Fix 2: weaken the reference:
use Scalar::Util 'weaken';
my $wa = {};
my $wb = {};
$wa->{other} = $wb;
weaken $wa->{other}; # won't prevent freeing
# $wb can now be reclaimed normally
# Detect: Devel::Leak / valgrind
# Prefer DAG-shaped structures (no cycles)

Weak References

Scalar::Util::weaken makes a reference not increase the count; once the target is freed, the weak reference becomes undef.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
use Scalar::Util qw(weaken isweak);
# Strong vs weak references:
my $data = {name => 'Rex'};
my $strong = $data; # strong ref
my $weak = $data;
weaken $weak; # convert to weak
say isweak($weak); # 1
# Drop the strong refs:
undef $data;
undef $strong;
# Weak ref now points to freed memory:
say defined $weak; # 0 (became undef)
# Cache that doesn't block reclamation:
my %cache;
my $obj = {id => 1};
$cache{1} = $obj;
weaken $cache{1};
# Check before use:
if (defined $cache{1}) {
say "cache hit";
}
# Weak references suit observer / cache scenarios

Scope-Based Release

Lexical variables and handles are automatically released/closed when out of scope; undef releases earlier.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
# Locals are freed when the sub returns:
sub work {
my $big = "x" x 1000000; # big string
process($big);
return 1;
# $big is released on return
}
# Use a block to end lifetime early:
{
my $tmp = load_data();
process($tmp);
} # $tmp is released when the block ends
# File handles auto-close:
sub read_all {
open my $fh, '<', 'data.txt' or die $!;
local $/;
return <$fh>; # $fh closes on return
}
# Manual release:
undef @big_array; # immediate release
# Referenced data stays alive as long as a reference exists:
my $keep = [1, 2, 3];
my $holder = {list => $keep}; # $keep stays alive

Autovivification

Assigning to an undefined reference auto-creates containers; convenient for writes, may mistakenly create when reading nested.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'say';
# Writes auto-create:
my $hash;
$hash->{a} = 1; # auto-creates a hashref
say $hash->{a}; # 1
my $arr;
$arr->[0] = "x"; # auto-creates an arrayref
# Multi-level autovivification:
my $deep;
$deep->{a}[0]{b} = 1; # created level by level
# Reading a missing key returns undef:
my %h;
say defined $h{missing} ? "present" : "absent";
# Reading nested may create intermediate layers too:
my $outer;
my $v = $outer->{x}{y}; # created {x}
# Avoid with short-circuit guard:
if ($outer && $outer->{x}) {
say "has value";
}
# To disable outright:
# use no autovivification;

Allocation and Speedups

Preallocate arrays, precompile regex, prefer join over interpolation; profile before optimizing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'say';
# Preallocate array capacity:
my @nums;
$#nums = 9999; # preallocate
for my $i (0..9999) {
$nums[$i] = $i;
}
# Precompile regexes for reuse:
my $re = qr/\d{4}-\d{2}-\d{2}/;
for my $line (@lines) {
$line =~ $re; # reuse compiled form
}
# Prefer join over .= in hot loops:
my $big = join '', @parts;
# Many .= operations are still slow:
my $s = "";
$s .= "x" for 1..1000;
# Plain text uses single quotes:
print 'plain text';
# Hashes give O(1) lookup:
my %lookup = map { $_ => 1 } @list;
# Profile with Devel::NYTProf
# Measure before optimizing โ€” don't micro-tune blindly

Memory Measurement

Devel::Size measures structure sizes; observe peak memory; stream large files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
# Measure structure size:
use Devel::Size qw(total_size);
my $struct = {
name => 'Rex',
tags => [qw(dev tools)],
};
say total_size($struct); # bytes
# A single variable:
Devel::Size::size($struct);
# Drill into large objects piece by piece:
# Dump the top level, then compare inner pieces
# Process memory monitor (Linux):
# Read VmRSS from /proc/$$/status
# Windows: observe via Task Manager
# Stream large files:
open my $fh, '<', 'big.log' or die $!;
while (my $line = <$fh>) {
process($line); # no whole-file load
}
# Reference: perldoc Devel::Size

Performance Benchmark

Benchmark module compares implementations; Time::HiRes provides high-resolution timing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use strict;
use warnings;
use feature 'say';
use Benchmark qw(cmpthese);
my @data = (1..1000);
# Compare two implementations:
cmpthese(1000, {
join_way => sub {
my $s = join ',', @data;
},
concat_way => sub {
my $s = "";
$s .= "$_" for @data;
},
});
# Prints per-second counts and relative speed
# Second-level timing:
my $t0 = time;
work();
my $elapsed = time - $t0;
say "elapsed $elapsed s";
# High-resolution timing:
use Time::HiRes qw(time);
my $t1 = time;
work();
my $ms = (time - $t1) * 1000;
say "elapsed $ms ms";
# Hot-spot profiling โ€” Devel::NYTProf

10.Object-Oriented Programming

Bless references, methods, inheritance, accessors, operator overloading, and Moose/Moo.

bless Construction

bless tags a reference with a class name to make it an object; an object is essentially a reference with a class name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
package Person;
sub new {
my $class = shift;
my $self = { name => 'unknown' };
return bless $self, $class;
}
# An object is a blessed reference:
my $p = Person->new();
say ref $p; # Person
# Default class is current package:
sub new_short {
my $self = {x => 1};
return bless $self; # current package
}
# An object is essentially a hash reference:
$p->{name} = 'Rex'; # mutate field directly
# Prefer encapsulating in methods (see accessors)
# Other reference types can be blessed too:
# bless [], 'Stack'; bless \$x, 'Ref'

Methods and Invocation

Left side of -> is the object, right side is the method; the first argument to the method is $self.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
package Counter;
sub new {
my $class = shift;
return bless { count => 0 }, $class;
}
sub inc {
my $self = shift;
$self->{count}++;
return $self->{count};
}
sub value {
my $self = shift;
return $self->{count};
}
package main;
my $c = Counter->new(); # constructor call
$c->inc(); # method call
$c->inc();
say $c->value(); # 2
# The first arg to a method is the invocant:
# Equivalent procedural call:
Counter::value($c); # 2
# Methods are inherited from the class and its ancestors
# -> can be chained: $c->inc()->inc()

Inheritance

use parent declares the parent class; subclass overrides methods of the same name; parent methods are reusable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use strict;
use warnings;
use feature 'say';
package Animal;
sub new {
my $class = shift;
my %a = @_;
return bless { name => $a{name} }, $class;
}
sub speak {
my $self = shift;
return "$self->{name} makes a sound";
}
sub eat {
my ($self, $food) = @_;
return "$self->{name} eats $food";
}
package Dog;
use parent -norequire, 'Animal';
sub speak { # override parent's speak
my $self = shift;
return "$self->{name} says woof";
}
package main;
my $d = Dog->new(name => 'Wangcai');
say $d->speak; # Wangcai says woof
say $d->eat('bone'); # inherited eat
# Method lookup: object's class โ†’ ancestor chain
# Multiple inheritance via @ISA or use parent

Constructors

new receives class name and arguments, returns a blessed object; multiple constructors can be provided.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
use strict;
use warnings;
use feature 'say';
package Point;
sub new {
my ($class, %args) = @_;
my $self = {
x => $args{x} // 0,
y => $args{y} // 0,
};
return bless $self, $class;
}
sub coords {
my $self = shift;
return ($self->{x}, $self->{y});
}
# Variant: build from a list:
sub from_list {
my ($class, $pair) = @_;
return $class->new(x => $pair->[0],
y => $pair->[1]);
}
package main;
my $p = Point->new(x => 3, y => 4);
my ($x, $y) = $p->coords;
say "$x,$y"; # 3,4
# Field defaults and validation belong in new
# Named arguments read more clearly with fat arrows

Encapsulation and Accessors

Encapsulate field access as methods; write accessors can validate; prevents external direct field modification.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'say';
package Account;
sub new {
my ($class, $name) = @_;
return bless {
name => $name,
money => 0,
}, $class;
}
# Reader accessors:
sub name { my $s = shift; return $s->{name} }
sub money { my $s = shift; return $s->{money} }
# Writer accessor (with validation):
sub deposit {
my ($self, $amt) = @_;
die "amount must be positive" if $amt <= 0;
$self->{money} += $amt;
}
package main;
my $acc = Account->new('alice');
$acc->deposit(100);
say $acc->name; # alice
say $acc->money; # 100
# Semantic checks belong inside methods
# Direct mutation $acc->{money} bypasses validation

Operator Overloading

use overload defines +, "" etc.; lets objects participate in numeric/string operations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
package Money;
use overload (
'""' => 'as_string', # stringify
'+' => 'add', # addition
'fallback' => 1,
);
sub new {
my ($class, $cents) = @_;
return bless { cents => $cents }, $class;
}
sub as_string {
my $self = shift;
return sprintf "ยฅ%.2f", $self->{cents} / 100;
}
sub add {
my ($a, $b) = @_;
return Money->new($a->{cents} + $b->{cents});
}
package main;
my $m1 = Money->new(199);
my $m2 = Money->new(1);
say $m1 + $m2; # ยฅ2.00
say "$m1"; # ยฅ1.99
# Overloadable: + - * / <=> cmp etc.
# Watch semantics for boolean / ref comparisons

Moo/Moose

Moo is a lightweight object system: has declares attributes, auto-generates accessors, extends inherits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'say';
# Moo (lightweight, recommended for small projects):
package Employee;
use Moo;
has name => (is => 'ro', required => 1);
has salary => (is => 'rw', default => 0);
sub annual {
my $self = shift;
return $self->salary * 12;
}
package Manager;
use Moo;
extends 'Employee';
has team_size => (is => 'ro', default => 0);
package main;
my $e = Employee->new(name => 'Rex');
$e->salary(5000);
say $e->name; # Rex
say $e->annual; # 60000
# Moo auto-generates accessors
# is => 'ro' read-only, 'rw' read-write
# Moose is the full version (types / roles / meta-programming)
# Use Moo for scripts; Moose for large projects

Roles and Composition

Moo::Role defines reusable method sets, included with `with`; composition over inheritance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use feature 'say';
# A role is a reusable method set:
package Walkable;
use Moo::Role;
sub walk { "walking" }
package Car;
use Moo;
with 'Walkable'; # compose the role
sub drive { "driving" }
package main;
my $car = Car->new;
say $car->walk; # walking
say $car->drive; # driving
# Compose multiple roles:
# with 'Walkable', 'Drivable';
# Same-named methods: later role in `with` wins
# Roles sidestep the diamond-inheritance problem
# Moose roles work the same way
# Without a framework, simulate with a hash of coderefs
# Prefer roles over inheritance for shared behavior

11.Error Handling

die/warn, eval catching, Try::Tiny, Carp, and custom exceptions.

die and warn

warn prints a warning without exiting; die prints and terminates; die can throw a string or object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
# warn โ€” to STDERR, no exit:
warn "Warning: config missing\n";
# die โ€” print and exit (non-zero status):
# die "Fatal error\n";
# Code after die does not run
# die inside eval can be caught:
eval {
die "Catchable error\n";
};
say "Caught: $@" if $@;
# die may carry an object or reference:
die { code => 500, msg => "server error" };
# Default exit code:
# die uses $! or 255; exit N for custom
# Common: $! holds the last system error
# Production: include $! in die messages to aid debugging

eval Catching

eval BLOCK catches exceptions; sets $@ on failure; returns the last expression of the block on success.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
# eval BLOCK catches exceptions:
eval {
die "boom";
};
if ($@) {
say "Caught: $@";
}
# On success $@ is empty
# eval return value:
my $result = eval {
compute(); # returns undef if it threw
};
# Returns undef on error
# Save $@ across nested evals:
my $outer_err;
eval {
eval { die "inner" };
$outer_err = $@;
};
# String eval is unsafe:
# eval $code; # not recommended
# Use only eval BLOCK for exception trapping
# Syntax errors surface at compile time

$@ Handling

$@ holds the most recent error; match on string or check object type; distinguish undef from exception.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
# $@ holds the most recent error:
eval { die "oops" };
my $err = $@; # copy before any other call
say $err if $err;
# Match by content:
eval { die "timeout" };
if ($@ && $@ =~ /timeout/) {
say "timeout handler";
}
# die-ing an object puts the object in $@:
eval {
die bless {msg => 'x'}, 'MyError';
};
if (ref $@ eq 'MyError') {
say $@->{msg};
}
# Distinguish undef result from thrown exception:
my $ok = eval { compute() };
if ($ok) { say "ok" }
# $@ is global โ€” don't rely across calls
# Modern Try::Tiny scopes the error to $_

Try::Tiny

try/catch/finally structure is clear; $_ carries the error; solves the $@ race condition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use strict;
use warnings;
use Try::Tiny;
# try / catch:
try {
die "boom";
} catch {
say "Caught: $_"; # error available as $_
};
# Return value:
my $result = try {
compute();
} catch {
warn "Failed: $_";
undef; # return undef on error
};
# Finally:
try {
risky();
} catch {
say "error";
} finally {
cleanup(); # always runs
};
# Preserves list context:
my @rows = try { fetch_rows() };
# Try::Tiny scopes the error to a local $_
# Avoids $@ being clobbered by nested evals
# Also catches errors from core modules like JSON

Carp Errors

croak/carp report the caller's location; confess/cluck include a stack trace. Friendlier for library code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
use Carp qw(carp croak confess);
# croak โ€” report at the caller's location:
sub open_data {
croak "Cannot open" unless -e "data.txt";
}
open_data(); # error points to the caller's line
# carp โ€” warning (caller's location):
sub deprecated { carp "deprecated" }
# confess โ€” full stack trace:
sub inner { confess "inner error" }
sub outer { inner() }
# Distinction:
# die / warn โ€” current line
# croak / carp โ€” caller's line
# confess / cluck โ€” caller + stack trace
# Library code prefers croak to help users locate issues

Custom Exceptions

die objects and check with ref/isa; overload "" for friendly stringification.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use strict;
use warnings;
use feature 'say';
package MyError;
sub new {
my ($class, %args) = @_;
return bless { %args }, $class;
}
sub as_string {
my $self = shift;
return "$self->{code}: $self->{msg}";
}
use overload ('""' => 'as_string');
package main;
sub risky_thing {
my $ok = shift;
die MyError->new(code => 500, msg => "failure")
unless $ok;
}
# Throw and catch:
eval { risky_thing(0) };
if (ref $@ && $@->isa('MyError')) {
say "Error code: $@->{code}"; # 500
}
# Simpler options:
# use Exception::Class;
# Or the Ouch module for one-liner throws
# Object exceptions carry structured info

autodie

autodie makes open and other syscalls die on failure, eliminating the need for `or die`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
use strict;
use warnings;
use autodie;
# open failures auto-die:
open my $fh, '>', 'out.txt';
# No need for `or die $!` anymore
# Covered syscalls:
# open close chdir system exec
# chmod mkdir rmdir rename unlink
# Scope to specific calls:
use autodie qw(open);
# Disable locally:
no autodie;
# Combine with eval to catch:
eval {
open my $f, '<', 'missing.txt';
};
if ($@) { say "open failed" }
# Best paired with Try::Tiny:
use Try::Tiny;
try {
open my $f, '<', 'x.txt';
} catch {
say "cannot open: $_";
};
# autodie ships with modern Perl (2.30+)

Error Handling Patterns

`or die` idiom, early validation, fail-fast return; top-level die, internal library returns.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
use strict;
use warnings;
use feature 'say';
# or die idiom:
open my $fh, '<', 'data.txt'
or die "open failed: $!";
# Validate arguments up front:
sub divide {
my ($a, $b) = @_;
die "division by zero" unless $b;
return $a / $b;
}
# Return a status instead of throwing:
sub parse {
my $s = shift;
return $s =~ /^\d+$/ ? $s + 0 : undef;
}
my $n = parse("12");
if (defined $n) {
say "number: $n";
} else {
say "invalid input";
}
# Fail fast:
sub load_config {
my $path = shift;
return undef unless -f $path;
open my $f, '<', $path or return undef;
local $/;
return <$f>;
}
# Top-level: die freely. Libraries: prefer return values.

12.File I/O

open read/write, diamond operator, slurp, encoding, paths, and JSON.

Open and Read

Three-argument open to read files; line-by-line while is most memory-efficient; -e/-f check file attributes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
# Three-argument open (recommended):
open my $fh, '<', 'data.txt' or die $!;
while (my $line = <$fh>) {
chomp $line;
say $line;
}
close $fh;
# Read all into an array:
open my $f2, '<', 'data.txt' or die $!;
my @lines = <$f2>;
close $f2;
# File attribute checks:
if (-e 'data.txt') { say "exists" }
-f 'data.txt'; # plain file
-d 'dir'; # directory
-r 'data.txt'; # readable
# Three-argument open is safer with odd filenames
# Line-by-line uses far less memory than slurping

Write and Append

> for overwrite, >> for append; print/printf to write handles; umask controls permissions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
use feature 'say';
# Overwrite:
open my $out, '>', 'out.txt' or die $!;
print $out "hello\n";
close $out;
# Append:
open my $log, '>>', 'app.log' or die $!;
print $log "time=2026 msg=start\n";
close $log;
# Formatted writes:
printf $out "%05d\n", 42;
# Write multiple lines in one shot:
open my $f, '>', 'data.txt' or die $!;
print $f @lines;
close $f;
# Default mode is 0666 & ~umask:
umask 022;
# Created automatically when missing
# Switch to binmode for binary data

Diamond Operator

<> reads from argument files, or STDIN if none; $. is the cumulative line number, $ARGV is the current file name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
# Diamond operator reads the argument files:
while (<>) {
chomp;
say "Read: $_";
}
# Run as:
# perl script.pl a.txt b.txt
# Without args reads STDIN:
# echo hi | perl script.pl
# Diamond on a specific handle:
open my $fh, '<', 'data.txt' or die $!;
while (<$fh>) {
print; # default outputs $_
}
# $. โ€” cumulative line counter across files:
while (<>) {
say "Line $. : $_";
}
# $ARGV โ€” current file name:
print "file: $ARGV\n" if eof;
# Suited to small line-oriented tools

Read Entire File

local $/ set to undef to read all at once; File::Slurper provides the read_text convenience function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use feature 'say';
# Native slurp:
open my $fh, '<', 'data.txt' or die $!;
local $/; # separator set to undef
my $content = <$fh>;
close $fh;
# File::Slurper (convenient):
use File::Slurper 'read_text';
my $text = read_text('data.txt');
# One-shot write:
use File::Slurper 'write_text';
write_text('out.txt', "hello\n");
# Reading binary:
# read_binary / write_binary
# Don't slurp large files:
# Eats all available memory โ€” stream line by line
# Combine with decode when an encoding is involved

binmode and Encoding

binmode for binary safety; <:utf8 layer for UTF-8 reading/writing; :raw removes the encoding layer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
# Binary-safe write:
open my $fh, '>', 'img.bin' or die $!;
binmode $fh;
print $fh pack("C*", 0..255);
close $fh;
# Open with an encoding layer:
open my $in, '<:encoding(UTF-8)', 'data.txt'
or die $!;
# Shortcut layer:
open my $in2, '<:utf8', 'data.txt' or die $!;
# Emit UTF-8:
binmode STDOUT, ':utf8';
# Drop the encoding layer:
binmode $fh, ':raw';
# Transcode after reading:
use Encode;
while (my $line = <$in>) {
print encode('UTF-8', $line);
}
# Encoding layers handle byte-level details
# Manually transcode mixed-encoding files

Standard Handles

STDIN/STDOUT/STDERR; duplicate handles, detect terminal, immediate flush.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'say';
# Standard handles:
print STDOUT "to stdout\n";
print STDERR "to stderr\n";
my $line = <STDIN>;
# Duplicate a handle:
open my $copy, '>&', STDOUT;
print $copy "duplicated\n";
# Detect TTY:
my $tty = -t STDOUT;
say $tty ? "terminal" : "pipe";
# Immediate flushing (no buffering):
$| = 1; # every print goes out
# Read a fixed number of lines:
for (1..3) {
my $l = <STDIN>;
last unless defined $l;
}
# Check close failure:
close $copy or die "close failed: $!";
# Redirection happens in the shell:
# perl script.pl > out.txt 2>&1

Path Operations

Cwd for current directory, File::Spec for cross-platform path joining, glob for file matching, File::Find for recursion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
use Cwd;
my $cwd = getcwd();
say $cwd;
use File::Spec;
my $path = File::Spec->catfile('data', 'sub', 'f.txt');
use File::Basename;
my ($name, $dir, $ext) = fileparse(
'data/app.pl', qr/\.[^.]*/);
# Absolute path:
my $abs = File::Spec->rel2abs('data.txt');
# Glob files:
for my $f (glob 'data/*.txt') {
say $f;
}
# Recursive traversal:
use File::Find;
find(sub { say $File::Find::name }, '.');
# Use catfile for path joining โ€” don't concat strings

JSON Processing

JSON::PP is a core module: encode/decode, pretty, UTF-8. JSON::XS for production performance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use feature 'say';
# JSON::PP core module (5.14+):
use JSON::PP;
my $json = JSON::PP->new->utf8->pretty;
# Encode:
my $data = { name => 'Rex', tags => ['dev'] };
my $str = $json->encode($data);
# Decode:
my $back = $json->decode($str);
say $back->{name};
# Convenience functions:
use JSON::PP qw(encode_json decode_json);
my $s = encode_json({a => 1}); # {"a":1}
my $h = decode_json('{"a":1}');
say $h->{a}; # 1
# Handle decode failures:
eval { decode_json("bad json") };
say "parse failed" if $@;
# For performance-sensitive code, use JSON::XS
# Structures must be plain Perl references

13.Common Pitfalls

The most common pitfalls in Perl daily development and the correct way to write them.

Forgetting use strict

Without strict, typos are silent and variables auto-globalize. strict is the safety baseline.

1
2
3
4
5
6
7
8
9
10
11
# BAD: skipping strict โ€” typos stay silent
$foo = 1; # typo $f00 is hard to spot
@array = (1, 2); # auto-global, no declaration
# GOOD: use strict forces declarations
use strict;
use warnings;
my $foo = 1;
# Typos become compile-time errors
# Three faces of strict: vars refs subs
# Runtime issues defer to warnings

Context Confusion

Scalar and list contexts differ: array in scalar context is its length. Use scalar explicitly.

1
2
3
4
5
6
7
8
9
10
11
12
# BAD: ignoring scalar/list context
my @arr = (1, 2, 3);
my $n = @arr; # 3 length of the array
my $s = @arr; # still 3
# GOOD: state the intent explicitly
my @arr2 = (1, 2, 3);
my $len = scalar @arr2; # explicit length
my @copy = @arr2; # list context โ€” copy
my $last = $arr2[-1]; # last element
# Expect a list? Use @. Expect a scalar? Use $.
# `scalar` forces scalar context

Forgetting chomp

<STDIN> includes the newline; forgetting chomp makes string comparisons always fail.

1
2
3
4
5
6
7
8
9
10
11
12
13
# BAD: forgot chomp โ€” comparisons always fail
my $line = <STDIN>; # trailing newline
if ($line eq "quit") { say "never reached" }
# GOOD: chomp before comparison
my $line2 = <STDIN>;
chomp $line2;
if ($line2 eq "quit") { say "exits" }
# chomp mutates $_ in place and returns the removed char count
# Numeric input should also be chomp-ed:
my $num = <STDIN>;
chomp $num;
my $value = $num + 0;

Mixed Comparison Operators

== for numeric comparison, eq for string comparison. Mixing them wrongly treats "10" and "010" as equal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# BAD: comparing strings with == โ€” wrong after numeric coercion
my $a = "10";
my $b = "010";
if ($a == $b) { say "falsely equal" } # both become 10
# GOOD: strings use eq
my $a2 = "10";
my $b2 = "010";
if ($a2 eq $b2) { say "different strings" }
# Numbers compare with ==:
if (10 == 10.0) { say "numerically equal" }
# Operator cheat sheet:
# == != < > numeric; eq ne lt gt string
# Mixing them produces nasty bugs

Unordered Hash Iteration

Hash internal order is undefined; relying on iteration order causes random bugs. Sort keys if order is needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# BAD: relying on hash iteration order
my %h = (b => 1, a => 2);
for my $k (keys %h) {
print "$k=$h{$k}\n"; # order is random
}
# GOOD: iterate in sorted-key order
my %h2 = (b => 1, a => 2);
for my $k (sort keys %h2) {
print "$k=$h2{$k}\n"; # a first, then b
}
# Hash internal order is undefined โ€” don't rely on it
# To preserve order, store keys in an array:
my @order = qw(a b c);
for my $k (@order) { print "$k=$h2{$k}\n" }

Reading Triggers Autovivification

Reading nested structures auto-creates intermediate references, silently growing memory. Short-circuit checks prevent this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# BAD: reading nested structures creates them by accident
my $config;
if ($config->{db}{host}) {
say "has config";
}
# The read promoted $config->{db} into a reference!
# GOOD: short-circuit guard
my $config2;
if ($config2 && $config2->{db}{host}) {
say "has config";
}
# The RHS doesn't evaluate when $config2 is undef
# No spurious structures get created
# Writes autovivifying is convenient (intentional)
# For reads โ€” guard with defined / exists first
# `no autovivification` to disable entirely

Confusing local and my

local temporarily overrides globals (restored after block); my is the lexical local variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# BAD: using local as a "local variable"
sub work {
local $temp = 5; # this writes to a package global
}
# GOOD: my for normal locals
sub work2 {
my $temp = 5; # lexical variable
return $temp;
}
# local's purpose is temporarily overriding special variables:
{
local $/; # temporarily undef the input separator
my $all = <$fh>; # slurp the whole file
}
# local also localizes $! and $@
# Don't pretend a global is a local with local

Outdated open Syntax

Two-argument open + bareword handle pollutes the symbol table and is unsafe. Use three-argument + lexical handle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# BAD: two-argument open + bareword handle
open FH, ">out.txt"; # pollutes symbol table, unsafe mode
# GOOD: three-argument open + lexical handle
open my $fh, '>', 'out.txt' or die $!;
print $fh "data\n";
close $fh;
# Reading โ€” same rule:
open my $in, '<', $user_file or die $!;
# Three args distinguish the mode from the filename
# Filenames starting with > won't be mis-parsed:
open my $f, '<', ">weird_name.txt";
# Lexical handles auto-close โ€” no leaks

Array in Scalar Context

Array in scalar context returns length, list returns last element. Use scalar to get length.

1
2
3
4
5
6
7
8
9
10
11
12
13
# BAD: assuming scalar context returns the first element
my @arr = (1, 2, 3);
my $x = @arr; # 3 (it's the length!)
my $y = (1, 2, 3); # 3 (last element of the list)
# GOOD: be explicit about context
my @arr2 = (1, 2, 3);
my $len = scalar @arr2; # length: 3
my ($first, @rest) = @arr2; # 1, (2,3)
my $last = $arr2[-1]; # 3
# Comma expression in scalar context returns the last item
# Use `scalar` explicitly for the length
# Use `@copy = @arr` when you want a copy

14.Multithreading

threads create threads, threads::shared shared data, locks, and queue synchronization.

Create Threads

threads->create starts a new thread; join waits for it to finish and returns the result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use strict;
use warnings;
use threads;
# Create threads:
my @threads;
for my $i (1..3) {
push @threads, threads->create(sub {
say "thread $i running";
return $i * 10;
});
}
# Wait and collect results:
for my $t (@threads) {
my $result = $t->join();
say "result: $result";
}
# join blocks until the thread finishes
# The thread object is freed after exit

join and Errors

join returns the thread's result; die inside the thread propagates to the main thread and can be caught with eval.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
use threads;
# join returns the value and the error:
my $t = threads->create(sub {
die "error inside thread\n" if rand > 0.5;
return "ok";
});
my $result = $t->join();
if ($result) {
say "returned: $result";
}
# die inside a thread makes the main thread die
# To catch in join, wrap with eval:
my $t2 = threads->create(sub {
die "boom\n";
});
eval { $t2->join() };
say "caught: $@" if $@;
# Unjoined threads leak โ€” detach to clean up on exit

Shared Data

threads::shared :shared variables are shared across threads; regular my variables are per-thread copies.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use strict;
use warnings;
use threads;
use threads::shared;
# Declare shared variables:
my $count :shared = 0;
my @list :shared = ();
my %seen :shared = ();
# Nested values of a shared hash must be shared too:
my %stats :shared;
# Concurrent ++ is atomic:
for (1..5) {
threads->create(sub { $count++ })->join();
}
say "count = $count"; # 5
# Only shared arrays / hashes are readable across threads
# Normal my variables are per-thread copies

Locks and Semaphores

lock protects critical sections; Thread::Semaphore throttles. Locks auto-release at scope end.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
use threads;
use threads::shared;
use Thread::Semaphore;
my $counter :shared = 0;
my $lock :shared;
# lock guards a critical section:
sub increment {
lock $lock; # other threads wait
$counter++;
# released automatically at scope end
}
# Semaphore throttling:
my $sem = Thread::Semaphore->new(2);
sub worker {
$sem->down(); # acquire a permit
do_work();
$sem->up(); # release
}
# lock is released at block end
# Deadlock risk: keep nested lock order consistent

Thread Queue

Thread::Queue is a thread-safe queue; dequeue blocks when empty; ideal for producer-consumer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use threads;
use Thread::Queue;
# Thread-safe queue:
my $q = Thread::Queue->new();
# Producer:
threads->create(sub {
$q->enqueue($_) for 1..5;
$q->end(); # signal end
});
# Consumer:
my $worker = threads->create(sub {
while (defined(my $item = $q->dequeue())) {
say "processing: $item";
}
});
$worker->join();
# dequeue blocks when the queue is empty
# enqueue supports batches:
# $q->enqueue(@items);
# Queues are great for producer-consumer patterns

Thread Arguments

Second argument and beyond of create are passed to the subroutine; lexical variables are per-thread copies.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
use threads;
# Thread copies args and lexicals:
my $base = 10;
my $t = threads->create(sub {
my $num = shift;
return $num + $base; # $base is a copy
}, 5);
say $t->join(); # 15
# Pass arguments per create:
my @results = map {
threads->create(sub {
my $x = shift;
return $x * $x;
}, $_)->join();
} 1..3;
# Lexicals are per-thread copies
# Sharing requires :shared

Parallel Tasks

Fixed thread pool picks up tasks; atomically allocate a shared index, avoiding per-task thread creation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use threads;
use threads::shared;
# Simple parallel task pool:
my @jobs = 1..10;
my @workers;
my $total :shared = 0;
my $next :shared = 0;
# Four fixed worker threads:
for (1..4) {
push @workers, threads->create(sub {
while (1) {
my $job;
lock $next;
last if $next >= @jobs;
$job = $jobs[$next++];
# Do time-consuming work:
$total += $job;
}
});
}
$_->join() for @workers;
say "sum: $total";
# Hand out chunks to a fixed thread pool
# Avoid the cost of a new thread per task

Thread Pool Pattern

Queue dispatch + result queue collection; call end multiple times to wake all blocked threads.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
use strict;
use warnings;
use threads;
use Thread::Queue;
# Fixed thread pool pattern:
my $q = Thread::Queue->new();
my $result_q = Thread::Queue->new();
# Consumer pool:
my @workers = map {
threads->create(sub {
while (defined(my $job = $q->dequeue())) {
my $out = process($job);
$result_q->enqueue($out);
}
})
} 1..4;
# Dispatch jobs:
$q->enqueue($_) for 1..100;
$q->end() for 1..4; # wake all consumers to exit
# Collect results:
my @results;
while (my $r = $result_q->dequeue()) {
push @results, $r;
}
$_->join() for @workers;
# Pools reuse threads โ€” minimum overhead
# Result queue avoids writing to shared structures directly

15.Network Programming

TCP sockets, HTTP clients, URL parsing, and DNS queries.

TCP Client

IO::Socket::INET creates a connection; print to send, <...> to read the response.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use strict;
use warnings;
use IO::Socket::INET;
# TCP client:
my $sock = IO::Socket::INET->new(
PeerHost => 'example.com',
PeerPort => 80,
Proto => 'tcp',
Timeout => 10,
) or die "cannot connect: $@";
# Send an HTTP request:
print $sock "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
# Read the response:
while (my $line = <$sock>) {
print $line;
}
close $sock;
# On failure $@ holds the reason
# Timeout prevents indefinite blocks

TCP Server

Listen on a port, accept connections; each connection can be echoed or handled by a child process.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
use IO::Socket::INET;
# TCP server:
my $server = IO::Socket::INET->new(
LocalAddr => '0.0.0.0',
LocalPort => 8080,
Proto => 'tcp',
Listen => 10,
ReuseAddr => 1,
) or die "cannot listen: $@";
say "listening on 8080";
while (my $client = $server->accept()) {
# Echo back each connection briefly:
my $line = <$client>;
print $client "got: $line";
close $client;
}
# Combine with fork to handle many connections
# ReuseAddr avoids "port still in use" on restart

HTTP GET

HTTP::Tiny is a core module; get returns status/content, can be paired with JSON::PP for parsing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use HTTP::Tiny;
my $ua = HTTP::Tiny->new(
timeout => 10,
agent => 'Mozilla/5.0',
);
# GET request:
my $res = $ua->get('https://api.example.com/data');
if ($res->{success}) {
say "status: $res->{status}";
print $res->{content};
} else {
warn "request failed: $res->{status}";
}
# Parse JSON response:
use JSON::PP;
my $data = decode_json($res->{content});
say $data->{name};
# HTTP::Tiny is a core module
# Configure options for redirects / proxies

HTTP POST

post sends arbitrary body; post_form URL-encodes and submits forms automatically.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use strict;
use warnings;
use HTTP::Tiny;
use JSON::PP;
my $ua = HTTP::Tiny->new();
# POST JSON:
my $payload = encode_json({
name => 'Rex',
age => 5,
});
my $res = $ua->post(
'https://api.example.com/users',
{
content => $payload,
headers => {
'Content-Type' => 'application/json',
},
},
);
# Form-encoded POST:
my $form = $ua->post_form(
'https://example.com/login',
{ user => 'admin', pass => 'secret' },
);
# Inspect the result:
if ($res->{success}) {
say "submitted";
}
# post_form URL-encodes for you
# File uploads via multipart or a file handle

URL Parsing

URI module parses and builds URLs; query_form reads/writes query parameters with auto-encoding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
use URI;
# Parse a URL:
my $uri = URI->new('https://user:[email protected]:8080/path?a=1&b=2#frag');
say $uri->scheme; # https
say $uri->host; # example.com
say $uri->port; # 8080
say $uri->path; # /path
# Query parameters:
my %query = $uri->query_form;
say $query{a}; # 1
# Build a URL:
my $u = URI->new('https://example.com/api');
$u->query_form(name => 'Rex', page => 2);
print $u; # query string already encoded
# Resolve a relative path:
my $abs = URI->new_abs('about.html', 'https://site.com/docs/');
# URI handles encoding escapes
# Don't hand-concatenate URLs โ€” let URI keep them correct

DNS Query

Net::DNS resolves A/MX/TXT records; search returns a response object; requires external module.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use strict;
use warnings;
use Net::DNS;
# Query an A record:
my $resolver = Net::DNS::Resolver->new();
my $query = $resolver->search('example.com');
if ($query) {
for my $rr ($query->answer) {
if ($rr->type eq 'A') {
say $rr->address;
}
}
} else {
warn "query failed: ", $resolver->errorstring;
}
# Other record types:
# $resolver->search('example.com', 'MX');
# $resolver->search('example.com', 'TXT');
# Or pin a specific server:
Net::DNS::Resolver->new(nameservers => ['8.8.8.8']);
# Net::DNS is non-core โ€” install it from CPAN

Socket Options

setsockopt sets low-level options; timeout controls blocking; IO::Select provides non-blocking waits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use IO::Socket::INET;
my $sock = IO::Socket::INET->new(
PeerHost => 'example.com',
PeerPort => 443,
Proto => 'tcp',
Timeout => 5,
) or die "connect failed";
# Set socket options:
$sock->setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1);
# Timeout:
$sock->timeout(10);
# Read / write:
print $sock "ping\n";
my $reply = <$sock>;
# Non-blocking read:
use IO::Select;
my $sel = IO::Select->new($sock);
if ($sel->can_read(2)) {
my $data = <$sock>;
}
# Buffer options like SO_RCVBUF are tunable
# IO::Select waits across multiple handles

REST Client

Wrap HTTP::Tiny for unified auth and JSON handling; errors thrown uniformly to caller.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use HTTP::Tiny;
use JSON::PP;
my $base = 'https://api.example.com/v1';
my $ua = HTTP::Tiny->new(timeout => 10);
# Generic REST wrapper:
sub api {
my ($method, $path, $body) = @_;
my $url = $base . $path;
my %opts = (headers => {
'Accept' => 'application/json',
'Authorization' => 'Bearer token123',
});
$opts{content} = encode_json($body) if $body;
my $res = $ua->request($method, $url, \%opts);
return decode_json($res->{content})
if $res->{success};
die "API error: $res->{status}";
}
my $user = api('GET', '/users/1');
my $created = api('POST', '/users',
{ name => 'Rex', age => 5 });
# Errors thrown uniformly to callers
# Pagination / retries as needed

16.Date and Time

time/localtime, strftime formatting, Time::Piece, and DateTime handling.

localtime Decomposition

time returns epoch; localtime decomposes into year/month/day/hour/min/sec; note month and year offsets.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use strict;
use warnings;
use feature 'say';
# localtime returns the time components:
my ($sec, $min, $hour, $mday, $mon, $year,
$wday, $yday, $isdst) = localtime;
# Months start at 0; year is an offset:
my $display = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
$year + 1900, $mon + 1, $mday,
$hour, $min, $sec;
say $display;
# gmtime returns UTC:
my @utc = gmtime;
# time returns the current epoch:
my $now = time;
say "current timestamp: $now";
# localtime depends on the system timezone
# strftime is usually clearer for formatting

strftime Formatting

POSIX::strftime uses %Y %m %d placeholders; %A %B output weekday and month names.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use strict;
use warnings;
use POSIX 'strftime';
# strftime โ€” format a time:
my $now = time;
my $fmt = strftime(
'%Y-%m-%d %H:%M:%S', localtime $now);
say $fmt; # 2026-08-02 12:34:56
# Common specifiers:
# %Y 4-digit year %y 2-digit
# %m month %d day %H hour %M min %S sec
# %A full weekday name %a short
# %B full month name %b short
# %j day of year (1-366)
my $date = strftime('%A %B %d', localtime);
my $custom = strftime('%Y/%m/%d %I:%M %p',
localtime); # 12-hour clock
# strftime is the C routine โ€” output follows locale

Timestamp Conversion

timelocal/timegm convert local/UTC time arrays back to epoch; note the offsets.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use strict;
use warnings;
use Time::Local;
# time returns the current epoch seconds:
my $now = time;
# Epoch to array (local or UTC):
my @local = localtime $now;
my @utc = gmtime $now;
# Array to epoch:
use Time::Local 'timelocal';
my $epoch = timelocal(
$sec, $min, $hour, $mday, $mon, $year);
# Parse epoch from a date string:
my $ts = timelocal(0, 0, 12, 2, 7, 126);
say "epoch for 2026-08-02 12:00: $ts";
# timegm handles UTC:
use Time::Local 'timegm';
my $utc_epoch = timegm(0, 0, 0, 1, 0, 126);
# Mind the month and year offsets (month -1, year -1900)

sleep Delay

sleep integer seconds; Time::HiRes supports fractional and microsecond delays; select for non-blocking waits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
# sleep โ€” block for the given seconds:
sleep 2;
say "awake";
# Fractional values (with Time::HiRes):
use Time::HiRes qw(sleep usleep);
sleep 0.5; # half a second
usleep 500000; # microseconds
# Periodic loop:
for (1..3) {
say "iteration $_";
sleep 1;
}
# Non-blocking delay trick:
# Use select with a zero timeout:
use IO::Select;
my $sel = IO::Select->new();
$sel->can_read(0.5); # wait but do not read
# sleep returns the remaining seconds when interrupted
# Production schedulers use Time::HiRes for precision

Elapsed Time Measurement

time for second-level timing; Time::HiRes for millisecond; DateTime for cross-time-zone/date differences.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
use feature 'say';
# Simple timing (seconds):
my $t0 = time;
do_work();
my $elapsed = time - $t0;
say "elapsed: $elapsed s";
# High-resolution timing:
use Time::HiRes qw(time);
my $s = time;
do_work();
my $ms = (time - $s) * 1000;
say "elapsed: $ms ms";
# Cross-time-zone / date deltas โ€” use DateTime:
use DateTime;
my $a = DateTime->now;
my $b = $a->clone->add(days => 3);
my $dur = $b - $a;
say $dur->in_units('hours'); # 72
# DateTime returns a Duration object
# Prevents DST gotchas

DateTime Object

DateTime for full time handling: construction, accessors, time zone conversion, and date arithmetic.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use DateTime;
# Construction and accessors:
my $dt = DateTime->now;
my $d2 = DateTime->new(
year => 2026, month => 8, day => 2,
hour => 12, minute => 30,
);
say $d2->ymd; # 2026-08-02
say $d2->hms; # 12:30:00
say $d2->year;
# Time zones:
my $tokyo = DateTime->now(time_zone => 'Asia/Tokyo');
my $utc = $tokyo->set_time_zone('UTC');
# Arithmetic:
my $later = $dt->clone->add(days => 7, hours => 3);
my $earlier = $dt->clone->subtract(months => 1);
# Format:
say $dt->format_cldr('yyyy-MM-dd HH:mm');
# DateTime is a non-core module โ€” covers most cases
# DST and TZ conversion are handled safely

String Parsing

Time::Piece::strptime parses date strings by format, returning epoch and differences.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use Time::Piece;
# Time::Piece provides strptime:
my $t = Time::Piece->strptime(
'2026-08-02 12:30:00', '%Y-%m-%d %H:%M:%S');
say $t->epoch;
# Parse other formats:
my $d = Time::Piece->strptime(
'02/Aug/2026', '%d/%b/%Y');
# Format output:
say $t->strftime('%Y/%m/%d');
# Epoch constructor:
my $now = localtime; # Time::Piece object
say $now->epoch;
say $now->year; # 2026
# Differences:
my $diff = $t - $now;
say $diff->days;
# Time::Piece ships with Perl core
# strptime throws on failure

Format Object

DateTime::Format::Strptime unifies parsing and formatting; RFC3339 handles ISO times.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use DateTime;
# DateTime formatting & parsing:
use DateTime::Format::Strptime;
my $format = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%d %H:%M:%S',
time_zone => 'UTC',
);
# Parse a string:
my $dt = $format->parse_datetime(
'2026-08-02 12:00:00');
# Format back to a string:
my $str = $format->format_datetime($dt);
say $str;
# Common patterns:
# %Y year %m month %d day
# %H hour %M minute %S second %z TZ offset
my $iso = $dt->format_cldr('yyyy-MM-dd HH:mm:ss');
# Concise RFC3339:
use DateTime::Format::RFC3339;
my $rfc = DateTime::Format::RFC3339->parse_datetime(
'2026-08-02T12:00:00Z');
# Use format objects for consistent parse/format

17.Processes and Commands

system/backticks for executing commands, environment variables, @ARGV, signals, and pipes.

Execute Commands

Backticks capture output; system executes without capturing; list form avoids shell injection.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use strict;
use warnings;
use feature 'say';
# Backticks โ€” capture output:
my $out = `ls -la`;
print $out;
# qx โ€” clearer with custom delimiter:
my $date = qx(date);
chomp $date;
# system โ€” run without capturing:
system("echo hello");
# system returns the exit status
# Backticks in scalar context return the whole output
# In list context โ€” split by newline:
my @lines = `cat file.txt`;
# Watch for escaping and injection:
# Use the list form to avoid shell interpolation:
system("ls", "-l");
# Don't paste user input into command strings

exec and system

exec replaces the current process; system waits for the child to finish; $? holds the exit status.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use strict;
use warnings;
# exec โ€” replace the current process:
# exec "ls", "-l"; # code after this does not run
# system โ€” wait for the child:
system("echo", "hi");
say "after system";
# Return value is the exit code:
my $status = system("true");
say "exit code: $status"; # 0
# $? holds the last status:
my $exit = $? >> 8; # actual exit code
my $sig = $? & 127; # signal that killed it
# exec and system don't capture output:
# Use backticks or a piped open for output
# exec fits when you replace the program entirely

Environment Variables

%ENV hash reads/writes environment variables, affecting child processes; local provides temporary isolation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'say';
# Read an env variable:
my $home = $ENV{HOME};
say "home: $home";
# Set an env variable:
$ENV{PATH} = '/usr/bin:/bin';
$ENV{LANG} = 'en_US.UTF-8';
# Walk every env variable:
for my $key (sort keys %ENV) {
print "$key=$ENV{$key}\n";
}
# Inherited by child processes:
$ENV{MODE} = 'test';
system("echo", $ENV{MODE});
# Delete:
delete $ENV{SECRET};
# %ENV affects all subsequent children
# Use local to scope changes:
{
local $ENV{DEBUG} = 1;
run_tests();
} # restored at block end

Command-Line Arguments

@ARGV is the argument list; complex argument parsing uses Getopt::Long declaratively.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
# @ARGV holds command-line arguments:
my ($file, $count) = @ARGV;
say "file: $file, count: $count";
# Arguments are just an array:
my @files = @ARGV;
for my $f (@files) {
say "processing: $f";
}
# shift defaults to @ARGV when no array given:
my $first = shift @ARGV;
# With no arguments, default to STDIN:
# while (<>) { ... }
# For parsing, prefer Getopt::Long:
use Getopt::Long;
my ($verbose, $name);
GetOptions(
'verbose' => \$verbose,
'name=s' => \$name,
);
say $verbose ? "verbose mode" : "quiet mode";
say "name: $name" if $name;
# $0 is the script name

Exit Status

exit N sets the exit code; 0 is success, non-zero is failure; END blocks run before exit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
# exit โ€” terminate with a status code:
exit 0; # success
# exit 1; # failure
# Without an argument, exit code is 0
# Conditional exit:
my $ok = check();
exit 1 unless $ok;
# die's exit code is 255 (or $!):
# Or just exit explicitly:
if ($err) {
print STDERR "failure: $err\n";
exit 2;
}
# $? and system:
system("false");
say $?; # 256 (128 + exit code)
my $code = $? >> 8; # 1
# Conventions with the shell:
# 0 success; non-zero failure
# END block runs before exit:
END { print "cleaning up\n" }
# On signal interrupt, read $? to learn the cause

Signal Handling

$SIG{INT} catches signals; handlers should only set flags, with the main loop responding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
# Catch signals:
$SIG{INT} = sub {
print "got interrupt, cleaning up\n";
exit 1;
};
$SIG{TERM} = 'DEFAULT'; # back to default
$SIG{QUIT} = 'IGNORE'; # ignore
# Idle loop:
while (1) { sleep 1 }
# Send a signal:
kill 'TERM', $pid;
# To a child process:
my $pid = fork();
if ($pid == 0) {
exit 0; # child
}
kill 'USR1', $pid;
# Keep signal handlers minimal:
# Only set a flag โ€” let the main loop act:
my $stop = 0;
$SIG{INT} = sub { $stop = 1 };
while (!$stop) { work() }
# %SIG is a global hash
# Windows has limited signal support

Process Pipes

-| reads child stdout, |- writes to child stdin; bidirectional interaction uses IPC::Open3.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
# Pipe-read command output:
open my $fh, '-|', 'grep error /var/log/app.log'
or die "cannot exec: $!";
while (my $line = <$fh>) {
chomp $line;
say "match: $line";
}
close $fh;
# Pipe-write to a command:
open my $out, '|-', 'gzip > out.gz'
or die "cannot exec: $!";
print $out "data to compress\n";
close $out;
# Alternative โ€” open2 / open3:
use IPC::Open3;
my $pid = open3(
my $in, my $out, my $err, 'cmd', 'args');
# '-|' reads child stdout
# '|-' writes child stdin
# Bidirectional interaction โ€” use IPC::Open3

Capture Output

Redirect to merge stdout/stderr; check $? for exit code; Capture::Tiny for structured capture.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
# Capture stdout + stderr:
my $out = `cmd 2>&1`;
# Capture only stderr:
my $err = `cmd 2>&1 1>/dev/null`;
# Check backtick's exit code:
my $result = `grep pattern file.txt`;
my $exit = $? >> 8;
if ($exit == 0) {
say "match found";
} else {
say "no match or error";
}
# Prevent timeouts during capture:
# Complex scenarios โ€” IPC::Open2 or IPC::Run
# Safe exec + capture:
use Capture::Tiny qw(capture);
my ($stdout, $stderr, $exit) =
capture { system("ls", "-l") };
say $stdout;
# Capture::Tiny is non-core
# Heavy command interaction โ€” consider IPC::Run

18.Regular Expressions

Pattern matching, character classes, quantifiers, capture groups, precompilation, and substitution.

Match Basics

=~ matches, !~ non-matches; anchors ^ $ \b position; i modifier ignores case.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
# Match operator =~ m//:
my $str = "hello world";
if ($str =~ /world/) {
say "contains world";
}
# Non-match !~:
if ($str !~ /xyz/) {
say "doesn't contain xyz";
}
# Anchors:
/^start/; # start of line
/end$/; # end of line
/\bword\b/; # word boundary
# Case-insensitive:
/hello/i;
# Capture once into a list:
my ($match) = $str =~ /(\w+) (\w+)/;
# More modifiers in the following topics

Match and Extract

g for global match; list context returns all matches; combine with grep for filtering arrays.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
# Test for a match:
if ($line =~ /error/) { ... }
# List context โ€” all matches:
my $str = "x1y2z3";
my @nums = $str =~ /(\d)/g; # 1 2 3
# Count matches:
my $count = () = $str =~ /\d/g;
# Combine with grep to filter:
my @words = qw(cat dog bird);
my @short = grep { length $_ < 4 } @words;
# Interpolation into the pattern:
my $pat = "a.c";
$str =~ /$pat/; # . matches any character
# Precompiled regex (see below):
my $re = qr/\d{3}-/;
$str =~ $re;
# grep / map implicitly iterate over $_

Character Classes

\d \w \s and their negations; custom [...] and negated [^...] character classes; quantifiers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
# Common character classes:
# \d digit [0-9]
# \w word [A-Za-z0-9_]
# \s whitespace [ \t\n\r\f]
# Uppercase negates: \D \W \S
# Custom character classes:
/[aeiou]/; # vowel
/[0-9a-fA-F]/; # hex
/[^0-9]/; # non-digit
# Quantifiers:
/a*/; # 0 or more
/a+/; # 1 or more
/a?/; # 0 or 1
/a{3}/; # exactly 3
/a{2,4}/; # 2 to 4
/a{2,}/; # 2 or more
# Grouping and alternation:
/(foo|bar)/;
/(ab)+/;
# Escape special characters:
/\$dollar/;
/1\+1/;
# The dot . matches any char except newline

Quantifiers and Backtracking

Greedy by default, non-greedy with ?; possessive quantifier + disables backtracking; nested quantifiers risk catastrophic backtracking.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use strict;
use warnings;
# Greedy vs non-greedy:
my $s = "<b>hi</b><b>yo</b>";
my @greedy = $s =~ /<b>.*<\/b>/g; # whole string
my @lazy = $s =~ /<b>.*?<\/b>/g; # two pieces
# Quantifiers:
# * greedy 0+ *? non-greedy
# + greedy 1+ +? non-greedy
# ? 0/1 ?? non-greedy
# {n,m} {n,m}? non-greedy
# Possessive quantifier (no backtracking):
my $big = "aaaaa";
$big =~ /a++a/; # fail (a++ consumed all)
$big =~ /a+a/; # succeed (backtracks)
# Line-anchor modifiers:
/^foo/m; # m makes ^$ match each line
/foo./s; # s makes . match newline too
# Perf note: nested quantifiers cause catastrophic backtracking
# Replace with fixed character classes when possible

Capture Groups

$1 $2 take captures in left-parenthesis order; (?:) non-capturing; (?<name>) named capture.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use strict;
use warnings;
use feature 'say';
# Capture groups $1 $2:
my $s = "name=alice age=30";
if ($s =~ /name=(\w+) age=(\d+)/) {
say "name: $1, age: $2";
}
# List context โ€” all captured values:
my ($name, $age) = $s =~ /name=(\w+) age=(\d+)/;
# Nested captures:
if ("abc123" =~ /((\w+)(\d+))/) {
say $1; # abc123
say $2; # abc
say $3; # 123
}
# Non-capturing group:
"hello" =~ /(?:he)(llo)/; # $1 is llo
# Named captures:
if ("Rex 5" =~ /(?<who>\w+) (?<num>\d+)/) {
say $+{who}; # Rex
say $+{num}; # 5
}
# $1-$9 are 1-indexed; unbound groups are undef
# Capture indices follow the order of left parentheses

Named Captures

(?<name>...) named group + %+ hash access; \k<name> backreference.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
# Named captures:
my $line = "2026-08-02 12:30";
if ($line =~ /(?<year>\d{4})-(?<mon>\d{2})-(?<day>\d{2}) (?<time>\d{2}:\d{2})/) {
say "year: $+{year}";
say "month: $+{mon}";
say "day: $+{day}";
say "time: $+{time}";
}
# Access via the %+ hash:
say $+{year};
# Backreference:
/(?<pair>\w)\k<pair>/; # repeated character
# Numeric backreference:
/(\w)\1/;
# Branch reset:
"ab" =~ /(?|a(b)|c(d))/; # $1 either way
# Named captures greatly improve readability
# %- holds all captures of the same name

Precompiled Regex

qr// compiles once and reuses; share inside modules for better performance in large loops; modifiers allowed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use strict;
use warnings;
use feature 'say';
# Precompile:
my $email_re = qr/[\w.]+@[\w.]+\.\w+/;
my $phone_re = qr/1[3-9]\d{9}/;
# Reuse:
my $text = "Contact a@b.com or 13800138000";
my @emails = $text =~ /($email_re)/g;
my @phones = $text =~ /($phone_re)/g;
say "emails: @emails";
# Build larger patterns from existing ones:
my $full = qr/($email_re|$phone_re)/;
# Share inside a module (compile once):
{
my $DATE_RE = qr/\d{4}-\d{2}-\d{2}/;
sub is_date { $_[0] =~ $DATE_RE }
}
# Precompiling avoids recompilation per match
# Big loops see noticeable gains
# qr accepts modifiers too:
my $ci = qr/foo/i;

Substitution and Translation

s/// substitution, g for global, i for case-insensitive; $1 etc. in replacement; tr/// for character translation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
# s/// substitution:
my $s = "apple apple";
$s =~ s/apple/orange/; # replace first only
say $s; # orange apple
# g โ€” global:
$s =~ s/apple/kiwi/g;
say $s; # kiwi kiwi
# i โ€” case-insensitive:
"HELLO" =~ s/hello/hi/i; # hi
# Using captures in the replacement:
my $date = "2026-08-02";
$date =~ s/(\d{4})-(\d{2})-(\d{2})/$2\/$3\/$1/;
# $1 $2 are usable in the replacement
# Counting via empty replacement:
my $count = ($s =~ s/x//g);
# tr/// is character-level translation (no regex):
my $t = "hello";
$t =~ tr/a-z/A-Z/; # HELLO
# s returns the replacement count in scalar context
# Watch the backslashes in the replacement part

19.Modules and Engineering

Module structure, use/require, option parsing, CPAN installation, and testing.

Module Structure

Package name corresponds to file path; Exporter for exports; trailing 1; makes `use` succeed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package My::Greet;
use strict;
use warnings;
# Export list:
use Exporter 'import';
our @EXPORT_OK = qw(greet);
our @EXPORT = qw(); # nothing exported by default
sub greet {
my $name = shift;
return "hello, $name";
}
# Version:
our $VERSION = '0.01';
1; # modules must return a true value
# Filename matches the package name:
# My/Greet.pm -> My::Greet
# @EXPORT auto-exports; @EXPORT_OK requires explicit request

use and require

use loads and imports at compile time; require loads at runtime; do executes a file without a symbol table.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
use feature 'say';
# use โ€” compile-time load + import:
use List::Util 'sum';
say sum(1, 2, 3);
# Load without importing:
use List::Util ();
# require โ€” runtime load:
require JSON::PP;
say JSON::PP::encode_json({a => 1});
# do โ€” execute a file (no symbol table):
my $cfg = do './config.pl';
# Check whether already loaded:
my $loaded = $INC{'List/Util.pm'};
# Load failures:
# use at compile time aborts on failure
# require throws โ€” catch with eval:
eval { require Missing::Module };
say "load failed: $@" if $@;

Module Installation

cpanm installs CPAN modules; core modules ship with Perl; cpanfile records dependencies.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
use strict;
use warnings;
use feature 'say';
# Find an installed module:
# perldoc -l Module
# Read its docs:
# perldoc Module::Name
# Install with cpanm:
# cpanm DateTime
# A specific version:
# Probe availability in a script:
use List::Util; # core module โ€” ships with Perl
# Core vs CPAN:
# Core: ships with Perl โ€” no install needed
# CPAN: install yourself (e.g. Moose)
# Prefer core for portability:
use JSON::PP; # core
use HTTP::Tiny; # core
use Time::Piece; # core
# List core module versions:
# corelist -a Time::Piece
# Record project deps in a cpanfile

Getopt::Long

Declarative option parsing: =s string, =i integer, flags, aliases, and negations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use strict;
use warnings;
use Getopt::Long;
# Declare options:
my ($verbose, $name, $count, $help);
GetOptions(
'verbose|v' => \$verbose,
'name=s' => \$name,
'count=i' => \$count,
'help|h' => \$help,
) or die "argument error";
# Use them:
if ($help) { print_usage(); exit 0 }
say $verbose ? "verbose mode" : "quiet mode";
say "name: $name" if defined $name;
say "count: $count" if defined $count;
# Common types:
# =s string =i integer =f float
# =s@ multiple values ! negation flag
# Long options accept --name=value and --name value
# Short option bundling: -vh equals -v -h
# GetOptions supports unambiguous abbreviations

Common CPAN Modules

Ecosystem like DateTime/Moose/DBI; search via MetaCPAN; use eval to probe availability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use strict;
use warnings;
use feature 'say';
# Common CPAN modules (install from CPAN):
# DateTime date / time handling
# Moose / Moo object systems
# DBI database access
# Mojo::UserAgent Web client
# JSON::XS fast JSON
# LWP::UserAgent classic HTTP client
# Text::CSV CSV parser
# XML::LibXML XML parser
# Install:
# cpanm Mojo::UserAgent
# Or probe inside a script:
my $has_json = eval { require JSON::XS; 1 };
# Sample (Mojolicious):
# use Mojo::UserAgent;
# my $ua = Mojo::UserAgent->new;
# my $tx = $ua->get('https://example.com');
# say $tx->result->body;
# Browse installed modules:
# perldoc perlmodlib
# Search on MetaCPAN:
# https://metacpan.org

Script and Command Line

shebang specifies interpreter; common Perl command-line options: -w, -e, -ne.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
# Executable script:
# chmod +x script.pl
# ./script.pl runs directly
# Portable shebang options:
# #!/usr/bin/env perl
# Uses whichever perl is first on $PATH (venv-friendly)
# Run from the command line:
# perl script.pl
# perl -w script.pl # enable warnings
# perl -Mstrict script.pl # enable strict
# perl -e 'print "hi"' # one-liner
# perl -ne 'print if /foo/' file # line-by-line
# Argument check:
my ($input, $output) = @ARGV;
die "Usage: $0 <in> <out>\n" unless $input && $output;
say "processing $input -> $output";
# $0 = script name; @ARGV = arguments
# Always start scripts with use strict + warnings

Packaging and Directory

lib/t directory layout, Makefile.PL build; -Ilib for local development loading.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package My::Lib;
use strict;
use warnings;
use Exporter 'import';
our @EXPORT_OK = qw(helper);
our $VERSION = '0.01';
sub helper { return 42 }
1;
# Directory layout:
# lib/My/Lib.pm the module itself
# t/01-basic.t tests
# Makefile.PL / cpanfile build instructions
# Changes / README changes and docs
# Local development loading:
# Point at lib/ via -I:
# perl -Ilib t/01-basic.t
# Or via PERL5LIB=lib
# Build and test (classic):
# perl Makefile.PL
# make
# make test
# Packaging and release:
# cpanm can install from a local directory
# Don't forget to update Changes when the version bumps

Test::More

plan declares test counts; is/ok/like assertions; prove -l runs tests in batch.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use strict;
use warnings;
use Test::More;
# Declare the test count:
plan tests => 4;
# Assertions:
is(add(2, 3), 5, "2+3=5");
ok(1 == 1, "truthy assertion");
isnt("a", "b", "not equal");
like("hello", qr/^he/, "matches");
sub add { $_[0] + $_[1] }
# Run the test file standalone:
# perl t/01-basic.t
# Or via prove:
# prove -l t/
# More assertions:
# is_deeply compares nested structures
# cmp_ok takes a custom comparison
# done_testing auto-counts tests
# Conditional skips:
# SKIP: { skip "unsupported on Windows", 1 }
# Test::More ships with the core

Official Links

Direct links to the official docs and resources.

About this Cheatsheet

This page is a self-contained Perl 5.38 cheatsheet, covering the language core and the most common built-in functions and a few CPAN modules โ€” roughly 80% of typical project usage. The content favors modern idioms: `use strict` + `use warnings` as default, postfix control flow, scalar vs list context, references for complex data structures, `say` / `state` / `signatures`. Perl is a multi-paradigm dynamic language first released by Larry Wall in 1987, famous for TMTOWTDI ("There's More Than One Way To Do It"), with deep roots in text processing, system administration, and web backends. Authoritative references include the `perldoc` shipped with Perl and the official [Perl documentation](https://perldoc.perl.org/). The 19 sections each focus on one topic โ€” from your first program to references, bless-based OO, and common pitfalls. Each section is split into 8โ€“14 sub-topics (each 5โ€“20 lines), giving ~150 topics in total. Code snippets are intentionally short and self-explanatory; you can copy and run them directly with `perl`. All processing happens entirely in your browser โ€” no uploads, no tracking. This page is part of GuruToolkit's free developer tool collection; the code snippets are free to use with no warranty.

Version 2.1.0