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.
#!/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.
#!/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.
#!/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.
#!/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.
#!/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.
#!/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.
#!/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.
#!/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
- my: lexical, visible within the enclosing block or file.
- our: package variable with a lexical alias in the current scope.
- local: dynamic, temporarily changes a package variable or element for the call chain.
- use strict: requires every variable to be declared or fully qualified.
In this article we have explained Perl scope rules, including my,
our, local, block and file scope, and nested subs.
Author
List all Perl tutorials.