Skip to content
VLSI Mentor

Perl Part I · Module 12 · Modules and Packages

local: Changing a Global Value Temporarily

The previous chapter ended with a problem it could not solve. An accessor protects a setting your module owns, and tells you nothing about how to change a setting you do not own — a built-in like the input record separator, or a variable belonging to a module you merely use. You cannot wrap those in an accessor, and assigning to them directly changes them for the rest of the program, which is a side effect at a distance of however many lines the run has left. local is the answer, and it is the third scoping keyword in the language after my and our. It saves the current value, installs yours for the duration of the enclosing block, and puts the original back on the way out. The measurement that makes the case is in section five: the restore happens when the scope exits, including when the block exits by dying — which is precisely the situation in which a wrong global setting left behind does the most damage and is hardest to trace.

Advanced14 min readPerlPart IModules

Part I · Module 12 · Chapter 12.8 · local: Changing a Global Value Temporarily

1. Save, Replace, Restore

Azvya Education Pvt. Ltd.VLSI Mentor
save-and-restore.pl
use strict;
use warnings;

our $LEVEL = "warn";

sub report { return "level is $LEVEL" }

printf "before : %s\n", report();
{
    local $LEVEL = "debug";
    printf "inside : %s\n", report();
}
printf "after  : %s\n", report();
print  "\nlocal put the old value back when the block ended\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
before : level is warn
inside : level is debug
after  : level is warn

local put the old value back when the block ended

Three reads of the same variable: warn, then debug, then warn again. Nothing restored it by hand — the block ending did that.

That is the whole feature, and it is worth noting what it is not. local did not create a new variable. There is exactly one $LEVEL throughout, and report() reads it by the same name the whole time. What changed is the value stored in it, and what local added is a promise to put the old value back.

2. Dynamic Scope: the Call Stack Decides

The property that makes local useful is also the one that makes it confusing, and it is worth measuring rather than describing.

Azvya Education Pvt. Ltd.VLSI Mentor
dynamic-scope.pl
use strict;
use warnings;

our $LEVEL = "warn";

# A subroutine defined ELSEWHERE still sees the localized value, because
# local changes the variable itself for the duration, not a copy.
sub deep   { return $LEVEL }
sub middle { return deep() }

printf "normally          : %s\n", middle();
{
    local $LEVEL = "debug";
    printf "inside the block  : %s\n", middle();
}
printf "after the block   : %s\n", middle();
print  "\nthat is DYNAMIC scope: who CALLS you decides what you see\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
normally          : warn
inside the block  : debug
after the block   : warn

that is DYNAMIC scope: who CALLS you decides what you see

deep is defined at the top of the file, nowhere near the block. Called from inside the block, it returned debug.

Compare that with my, which is lexical: a my variable is visible to code that is written inside its braces, and a subroutine defined elsewhere can never see it. local is dynamic: the value is visible to everything that runs while the block is on the stack, no matter where that code was written.

So the two keywords answer different questions. my asks "which text can see this name?" and the answer is fixed when you write the program. local asks "which execution can see this value?" and the answer depends on the call stack at run time.

3. It Only Works on Package Variables

Azvya Education Pvt. Ltd.VLSI Mentor
cannot-local-a-my.pl
use strict;
use warnings;

my $lexical = "mine";

my $ok = eval 'local $lexical = "changed"; 1';
if (!$ok) {
    my ($first) = split /\n/, $@;
    $first =~ s/ at .* line \d+.*$//;
    printf "local on a my variable : %s\n", $first;
}
printf "the variable is still   : %s\n", $lexical;
print  "\nlocal works on PACKAGE variables only\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
local on a my variable : Can't localize lexical variable $lexical
the variable is still   : mine

local works on PACKAGE variables only

Can't localize lexical variable $lexical — a clear and specific error.

That constraint is not a limitation so much as a definition. local works by saving the value held in a package variable's storage and restoring it later; a my variable has no package storage to save. If you want a temporary value in a lexical, the tool is a nested my in an inner scope, which creates a genuinely separate variable.

So the three keywords divide cleanly:

  • my — a new, private variable, visible to enclosing text.
  • our — a lexical alias to a package variable, so you can write the short name.
  • local — a temporary value in an existing package variable, visible to everything that runs.

local is the only one of the three that does not declare anything.

4. The Canonical Use: Reading a Whole File

Azvya Education Pvt. Ltd.VLSI Mentor
slurp-with-local.pl
use strict;
use warnings;
use File::Temp qw(tempfile);

my ($fh, $path) = tempfile(UNLINK => 1);
print $fh "line one\nline two\nline three\n";
close $fh;

# $/ is the input record separator. Setting it to undef makes <> slurp.
my $whole;
{
    open my $in, "<", $path or die $!;
    local $/;                     # undef, for this block only
    $whole = <$in>;
    close $in;
}
printf "slurped %d characters, %d newline(s)\n", length $whole, ($whole =~ tr/\n//);

# Outside the block, $/ is back to "\n", so <> is line-at-a-time again
open my $in2, "<", $path or die $!;
my $first = <$in2>;
close $in2;
$first =~ s/\n/\\n/;
printf "after the block, one read gives : [%s]\n", $first;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
slurped 29 characters, 3 newline(s)
after the block, one read gives : [line one\n]

Inside the block, one read returned all 29 characters including three newlines. Outside it, one read returned a single line.

$/ is the input record separator, and the readline operator uses it to decide where a "record" ends. Setting it to undef means "there is no separator", so a read consumes everything. local $/; with no assignment sets it to undef, which is the idiom you will see everywhere.

This is the right way to slurp, and the reason is the contrast with the alternative. Writing $/ = undef; at the top of a script changes the behaviour of every read in the program, including ones inside modules you called. A while (my $line = <$fh>) loop somewhere else would silently become a single-iteration loop reading the entire file into $line. The bug would appear in code you did not touch.

Chapter 11.16's measurements are the other half of this: once you have slurped, your string contains newlines, so ^ and $ stop meaning what they meant and you need /m.

Other built-ins worth localising the same way: $, and $\ for output separators, $" for list interpolation, and $| for autoflush on the currently selected handle. All of them are globals that code you did not write may read.

5. Restored Even When the Block Dies

This is the measurement that makes local better than doing it by hand.

Azvya Education Pvt. Ltd.VLSI Mentor
restored-even-on-die.pl
use strict;
use warnings;

our $LEVEL = "warn";

my $ok = eval {
    local $LEVEL = "debug";
    die "something went wrong\n";
    1;
};
printf "eval survived      : %s\n", ($ok ? "yes" : "no");
printf "\$LEVEL after a die : %s\n", $LEVEL;
print  "\nthe restore happens on scope exit, including an exceptional one\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
eval survived      : no
$LEVEL after a die : warn

the restore happens on scope exit, including an exceptional one

The block died. The eval caught it. And $LEVEL is back to warn.

Consider the hand-written equivalent, which is what people write before they know about local:

my $saved = $LEVEL; $LEVEL = "debug"; ...do the work...; $LEVEL = $saved;

That works until the middle part throws, and then the restore line never runs and the global is left changed for the rest of the program. It is the same class of bug as a file handle that is not closed on an error path, and it is worse in one way: a leaked setting produces wrong behaviour rather than a resource leak, somewhere else, later.

local ties the restore to scope exit, which happens on a return, a last, a die, or simply reaching the closing brace. There is no path out of the block that skips it.

6. Localising One Element

A detail worth knowing because it reads strangely the first time: local works on a single hash or array element, not just a whole variable.

local $ENV{PATH} = "/safe/bin"; changes one environment variable for the block and restores it afterwards — which is how you run a child process with a modified environment without disturbing the rest of the program. The same form works for local $SIG{__WARN__} = sub { ... } to install a warning handler temporarily, with the caveat from Chapter 11.4 that compile-time warnings are already past by then.

What you cannot do is local a hash element that does not exist and expect it to vanish cleanly in every Perl version; the element may be left in place with an undefined value. If that matters, delete it explicitly after the block.

7. What To Remember

  • local saves a package variable's value, installs yours for the enclosing block, and restores the original on the way out. Measured: warn, debug, warn.
  • It is dynamic scope. A subroutine defined elsewhere, called from inside the block, sees the localized value — measured. my is lexical and answers a different question.
  • It only works on package variables. local on a my variable is Can't localize lexical variable.
  • The three keywords: my declares a private variable, our declares an alias to a package variable, local declares nothing and temporarily replaces a value.
  • local $/; is the correct slurp, and local is what stops it breaking every other read in the program.
  • The restore happens on any scope exit, including a die. Measured: after a die inside the block, the global was back to its old value. A hand-written save-and-restore leaks on the error path.
  • Also localise $, $\ $" $|, and any module global that code you do not control reads.
  • local $ENV{PATH} and local $SIG{__WARN__} localise one element, which is the usual way to adjust an environment or install a temporary handler.
  • Use it for globals you do not own. For your module's own settings, Chapter 12.7's accessor is better, because it keeps the change visible.

Chapter 12.9 turns outward to the modules that already exist. Before writing a directory walk, a path manipulation, or an option parser by hand, there is a core module that does it — and the chapter is a tour of the nine worth knowing by name.

Where this fits

Part of the Perl curriculum.