Perl Cheatsheet

Control Flow and Subs

Use this Perl reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

if / elsif / else

if ($score >= 90) {
    print "A\n";
} elsif ($score >= 80) {
    print "B\n";
} else {
    print "practice\n";
}

print "ok\n" if $ok;       # statement modifier

Loops

for my $item (@items) {
    print "$item\n";
}

for (my $i = 0; $i < @items; $i++) {
    print "$i: $items[$i]\n";
}

while (my $line = <STDIN>) {
    chomp $line;
    next if $line eq "";
    last if $line eq "quit";
}

Loop Control

next;                         # skip to next iteration
last;                         # break out of loop
redo;                         # restart current iteration without condition

Subroutines with Signatures

Signatures are stable since Perl 5.36 and enabled by use v5.36;.

use v5.36;

sub greet ($name, $greeting = "Hello") {
    return "$greeting, $name";
}

sub total (@nums) {           # slurpy trailing array
    my $sum = 0;
    $sum += $_ for @nums;
    return $sum;
}

sub configure (%opts) {       # slurpy trailing hash
    return $opts{port} // 3000;
}

say greet("Ada");             # Hello, Ada

Legacy code (and code that must run on old Perls) unpacks @_ by hand:

sub greet {
    my ($name) = @_;
    return "Hello, $name";
}

state and Lexical Scope

my $outer = 1;                # lexical: visible to end of enclosing block/file
sub f {
    my $inner = 2;            # visible only inside f
    return $outer + $inner;
}

sub next_id {
    state $id = 0;            # initialized once, persists across calls
    return ++$id;             # state needs use v5.10+ or use feature 'state'
}

local and Dynamic Scope

local gives a package global (usually a special variable) a temporary value that is restored when the enclosing scope exits.

{
    local $/;                 # slurp mode inside this block only
    my $content = <$fh>;
}

{
    local $ENV{TZ} = 'UTC';   # works on hash/array elements too
    run_report();
}

wantarray and Context

my @all = get_items();        # list context
my $n = @items;               # array in scalar context yields its length
my $count = () = $s =~ /x/g;  # count matches via forced list context

sub flexible {
    return wantarray ? (1, 2, 3) : "scalar";
    # wantarray: true in list context, false in scalar, undef in void
}

Exceptions

# classic: eval + $@ (works everywhere)
my $result = eval { risky() };
if ($@) {
    warn "failed: $@";        # check $@ immediately after eval
}

die "no config file\n";       # trailing \n suppresses "at line N"
die { code => 404 };          # die with a ref for structured errors

Native try/catch blocks are stable since Perl 5.40 (on 5.34-5.38 add use feature 'try'; no warnings 'experimental::try';):

use v5.40;

try {
    risky();
}
catch ($e) {
    warn "failed: $e";
}

Try::Tiny from CPAN works on any Perl:

use Try::Tiny;

try {
    risky();
} catch {
    warn "failed: $_";        # the error is in $_
} finally {
    cleanup();
};                            # trailing semicolon required