ZetCode

Perl scope

last modified October 8, 2026

In this article we explain variable scope in Perl. Scope determines where a variable is visible in a program. We cover lexical variables declared with my, package variables declared with our, dynamically scoped package variables changed with local, and the rules for blocks, files, and nested subroutines.

This article complements the Perl variables tutorial. Here we focus on where variables are visible and how long they live.

Lexical scope with my

A variable declared with my is lexically scoped. It is visible from the point of its declaration to the end of the enclosing block, and it is not accessible from outside that block.

block.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

{
    my $msg = "inside block";
    say "Inside: $msg";
}

my $msg = "outside block";
say "Outside: $msg";

The first $msg lives only inside the braces. The second $msg is declared at file level and is unrelated to the first one.

{
    my $msg = "inside block";
    say "Inside: $msg";
}

The bare block creates a new scope. The my variable is created when the block is entered and is discarded when the block ends.

$ ./block.pl
Inside: inside block
Outside: outside block

Nested block shadowing

An inner my declaration with the same name hides the outer variable for the rest of the inner block. When the inner block ends, the outer variable is visible again.

shadow.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my $x = 10;

{
    my $x = 20;
    say "Inner x: $x";

    {
        my $x = 30;
        say "Innermost x: $x";
    }

    say "Inner x again: $x";
}

say "Outer x: $x";

Each block declares its own $x. The innermost declaration shadows the others only within its own braces.

$ ./shadow.pl
Inner x: 20
Innermost x: 30
Inner x again: 20
Outer x: 10

Package variables with our

A variable declared with our is a package variable, stored in the symbol table under the current package name. The our declaration also creates a lexical alias, so the short name can be used within the scope of the declaration. The full name, such as $Counter::count, works from anywhere.

our.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

package Counter;

our $count = 5;

sub show {
    say "Counter::count is $count";
}

package main;

Counter::show();

$Counter::count = 42;
Counter::show();

Inside package Counter, the short name $count refers to $Counter::count. From main, we reach the same variable through its full name, and the change is seen by Counter::show.

package Counter;

our $count = 5;

The our declaration makes $count refer to the package variable $Counter::count for the rest of the enclosing lexical scope.

$ ./our.pl
Counter::count is 5
Counter::count is 42

Dynamic scope with local

The local keyword works on package variables. It saves the current value, assigns a temporary one, and restores the saved value when the enclosing block exits. Subroutines called during that time see the temporary value. This is dynamic scope: it depends on the call chain at run time, not on where the code is written.

local_sub.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

our $level = "global";

sub show_level {
    say "level: $level";
}

sub with_local {
    local $level = "temporary";
    show_level();
}

show_level();
with_local();
show_level();

The with_local subroutine temporarily replaces the value of $level. The function show_level sees the temporary value because it is called while the local is in effect.

sub with_local {
    local $level = "temporary";
    show_level();
}

When with_local returns, the previous value of $level is restored automatically.

$ ./local_sub.pl
level: global
level: temporary
level: global

Local on a hash element

local can also be applied to a single element of a package hash. Only that element is changed, and it is restored on exit.

local_hash.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

our %config = (debug => 0, verbose => 1);

sub report {
    say "debug=$config{debug} verbose=$config{verbose}";
}

sub run_verbose {
    local $config{debug} = 1;
    report();
}

report();
run_verbose();
report();

Only debug is changed inside run_verbose. The verbose key is untouched, and the original value of debug is restored after the call.

$ ./local_hash.pl
debug=0 verbose=1
debug=1 verbose=1
debug=0 verbose=1

Note that local cannot be used on a my variable. Lexical variables are not part of the package symbol table, so they have no dynamic value to save and restore.

my variables in loops

A my variable declared inside a loop body is created afresh on each iteration. This matters when a closure captures the variable, because each closure keeps its own copy.

loop.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

my @subs;

for my $i (1 .. 3) {
    my $square = $i * $i;
    push @subs, sub { return "$i squared is $square" };
}

say $_->() for @subs;

The loop variable $i and the my variable $square are both new in every iteration. Each anonymous sub keeps the values from its own iteration.

push @subs, sub { return "$i squared is $square" };

The anonymous subroutine is a closure. It captures the lexical variables that were in scope when it was created.

$ ./loop.pl
1 squared is 1
2 squared is 4
3 squared is 9

Nested named subs and file scope

A named subroutine can see lexical variables that were in scope where it was defined. Here reveal is defined inside a block, and it keeps access to $secret even after the block has finished. A my variable at file level is visible from its declaration to the end of the file.

nested_sub.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

{
    my $secret = "hidden value";

    sub reveal {
        return $secret;
    }
}

say "Revealed: ", reveal();

The variable $secret is not visible at file level, but the named subroutine reveal was compiled inside its scope and still reads it. Code outside the block cannot refer to $secret directly.

$ ./nested_sub.pl
Revealed: hidden value

use strict and declarations

With use strict, Perl requires every variable to be declared with my or our, or to be referred to by its fully qualified package name. An undeclared variable is a compile-time error, which helps catch typos and accidental globals. The use v5.34.0 line also enables strict mode.

strict.pl
#!/usr/bin/perl

use strict;
use warnings;
use v5.34.0;

$name = "Alice";
say "Hello, $name";

The variable $name is never declared, so the program does not run. Perl reports each undeclared use and stops before executing any code.

$ ./strict.pl
Global symbol "$name" requires explicit package name (did you forget to declare "my $name"?) at ./strict.pl line 7.
Global symbol "$name" requires explicit package name (did you forget to declare "my $name"?) at ./strict.pl line 8.
Execution of ./strict.pl aborted due to compilation errors.

To fix the program, declare the variable with my before using it, as in my $name = "Alice";.

Scope summary

In this article we have explained Perl scope rules, including my, our, local, block and file scope, and nested subs.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Perl tutorials.