You rarely need to use local to localize global variables, but in some cases you cannot use my and then local is needed.

In other words: In almost every case when you want to create variable that is scoped to some block you should use my.

Let's see a few examples:

Slurp mode

examples/slurp.pl

use strict;
use warnings;

sub slurp {
    my $file = shift;
    open my $fh, '<:encoding(utf8)', $file or die "Cannot open '$file' $!";
    local $/ = undef;
    my $cont = <$fh>;
    close $fh;
    return $cont;
}

In the so-called slurp mode wer read the entire content of a file into a single scalar variable. In order to do that we need to set the $/ to be undef. We use local to limit the scope.

Change the LIST_SEPARATOR

In the rare case when you'd like to change the character that is inserted between array elements in string interpolation, you can change $" the $LIST_SEPARATOR variable.

examples/list_separator.pl

use strict;
use warnings;

my @planets = qw(
   Mercury
   Venus
   Earth
   Mars
   Jupiter
   Saturn
   Uranus
   Neptune
);

print "@planets\n";  # Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune

$" = '-';
print "@planets\n"; # Mercury-Venus-Earth-Mars-Jupiter-Saturn-Uranus-Neptune

{
    local $" = '-^-';
    print "@planets\n"; # Mercury-^-Venus-^-Earth-^-Mars-^-Jupiter-^-Saturn-^-Uranus-^-Neptune
}

print "@planets\n"; # Mercury-Venus-Earth-Mars-Jupiter-Saturn-Uranus-Neptune

$" = '';
print "@planets\n"; # MercuryVenusEarthMarsJupiterSaturnUranusNeptune

Local on package variables

examples/local.pl

use strict;
use warnings;
use 5.010;

our $x = 1;
my $y = 1;

say "x $x";
say "y $y";

{
    local $x = 2;
    my $y = 2;

    say "x $x";
    say "y $y";

    show_vars();
}

say "x $x";
say "y $y";

exit();

sub show_vars {
    say "x $x in show_vars";
    say "y $y in show_vars";
}


x 1
y 1
x 2
y 2
x 2 in show_vars
y 1 in show_vars
x 1
y 1

The only difference is when you call a function from inside the block where you local-ized or my-ed the global variable.

In both cases the variable inside the block hides the variable outside the block, but when we call a function from that block the behavior changes. In the case of my the change is scoped to the enclosing block In the case of our and local the changes is scoped to the enclosing block and to anything called from that block. We inherit the new value.

See also the official documentation.