Perl special variables
last modified October 20, 2026
Perl has a large set of predefined variables that control the interpreter or
expose information about the program and its environment. Many of them have
punctuation names, such as $_ or $!, and are global.
This article covers the ones used most often.
The default variable $_
The variable $_ is the default scalar. Many constructs set it
implicitly and many functions use it when no argument is given:
for, map, grep, print,
length and pattern matches.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my @words = qw(apple banana cherry);
for (@words) {
say "word: $_";
}
my @lengths = map { length } @words;
say "lengths: @lengths";
my @long = grep { length > 5 } @words;
say "long: @long";
for (@words) {
s/a/A/;
}
say "modified: @words";
The for loop without a loop variable aliases each element to
$_. In map { length } @words the call to
length has no argument, so it measures $_. Because
$_ is an alias, the substitution s/a/A/ changes the
elements of the original array.
$ perl default_var.pl word: apple word: banana word: cherry lengths: 5 6 6 long: banana cherry modified: Apple bAnana cherry
Program name and arguments: $0 and @ARGV
The variable $0 holds the name of the running script and the array
@ARGV holds the command-line arguments. Outside of subroutines,
shift without an argument works on @ARGV.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
say "script: $0";
say "args: ", scalar @ARGV;
for my $i (0 .. $#ARGV) {
say " ARGV[$i] = $ARGV[$i]";
}
my $first = shift;
say "first: ", $first // 'none';
say "left: @ARGV";
The loop goes over the indexes 0 .. $#ARGV. The call to
shift removes the first argument and returns it, and the defined-or
operator // supplies a default when no arguments were passed.
Inside a subroutine the same shift operates on @_
instead.
$ perl args.pl one two three script: args.pl args: 3 ARGV[0] = one ARGV[1] = two ARGV[2] = three first: one left: two three
Environment: %ENV
The hash %ENV contains the environment variables of the process.
Changes to it are inherited by child processes started with system,
backticks or exec.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
say "HOME: $ENV{HOME}";
say "missing: ", $ENV{NO_SUCH_VAR} // 'unset';
$ENV{GREETING} = 'hello';
system('echo "child sees: $GREETING"');
{
local $ENV{GREETING} = 'temporary';
system('echo "child sees: $GREETING"');
}
system('echo "child sees: $GREETING"');
Reading a missing key returns undef, so the defined-or operator
gives a default. Setting $ENV{GREETING} makes the value visible to
the shell started by system. With local the change is
temporary and the previous value is restored at the end of the block.
$ perl env.pl HOME: /home/jano missing: unset child sees: hello child sees: temporary child sees: hello
Errors: $! and $@
The variable $! holds the last system error. It is a dual-valued
variable: in string context the message, in numeric context the
errno number. The variable $@ holds the exception
caught by eval, and is empty if no error occurred.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my $file = '/no/such/file.txt';
open(my $fh, '<', $file) or say "open failed: $!";
say "errno number: ", $! + 0;
eval { die "something went wrong\n" };
print "caught: $@" if $@;
eval { my $x = 1 / 0; };
print "caught: $@" if $@;
eval { 1 } and say "no error, \$@ is empty: '", $@, "'";
The failing open sets $! to the message No such
file or directory, and adding zero to it gives the number 2. The
die call inside eval stores its message in
$@. Since the message ends with a newline, no location is appended;
the division by zero error adds the file and line. A successful
eval resets $@ to an empty string.
$ perl errors.pl open failed: No such file or directory errno number: 2 caught: something went wrong caught: Illegal division by zero at errors.pl line 16. no error, $@ is empty: ''
Output and process variables
A group of variables controls how output is formatted: $, is
inserted between the arguments of print, $\ is
appended at the end, $" separates the elements of an interpolated
array, and $| turns on autoflush. Others describe the process:
$$ is the process id, $^V the Perl version,
$^O the operating system and $^T the start time.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my @a = (1, 2, 3);
{
local $, = '-';
local $\ = "!\n";
print @a;
}
{
local $" = ', ';
say "list: @a";
}
{
local $| = 1;
say "autoflush: $|";
}
say "pid is numeric: ", ($$ =~ /^\d+$/ ? 'yes' : 'no');
say "perl version: $^V";
say "os: $^O";
say "program start is recent: ", (time - $^T < 5 ? 'yes' : 'no');
say "line: ", __LINE__, " file: ", __FILE__;
Each output variable is changed with local inside a block, so the
old value comes back after the block. The first block prints 1-2-3!
with a custom separator and terminator. The second one joins an interpolated
array with a comma. The remaining lines read process information, together with
the compile-time tokens __LINE__ and __FILE__.
$ perl output_vars.pl 1-2-3! list: 1, 2, 3 autoflush: 1 pid is numeric: yes perl version: v5.34.0 os: linux program start is recent: yes line: 29 file: output_vars.pl
Regex variables
After a successful match Perl sets the capture variables $1,
$2 and so on, plus $& for the whole match,
$` for the text before it and $' for the text after
it. The arrays @- and @+ hold the start and end
offsets of the match and of each group.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my $s = 'Name: John Smith, age 42';
if ($s =~ /(\w+)\s+(\w+),\s+age\s+(\d+)/) {
say "first: $1";
say "last: $2";
say "age: $3";
say "match: '$&'";
say "before: '$`'";
say "after: '$''";
say "whole match: $-[0] to $+[0]";
say "group 2: $-[2] to $+[2]";
}
The three groups capture the first name, last name and age. The match starts
after the prefix Name: , which appears in $`, and the
offsets $-[0] and $+[0] give the span of the whole
match. Index 2 in the arrays refers to the second group. These variables are
only valid until the next successful match in the same scope, so copy them if
they are needed later.
$ perl regex_vars.pl first: John last: Smith age: 42 match: 'John Smith, age 42' before: 'Name: ' after: '' whole match: 6 to 24 group 2: 11 to 16
Input variables: $. and $/
The variable $. is the line number of the last filehandle read. The
variable $/ is the input record separator, a newline by default.
Setting it to undef reads the whole file at once, and setting it to
an empty string reads paragraphs.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my $text = "line1\nline2\n\nline4\nline5\n";
open(my $in, '<', \$text) or die $!;
while (<$in>) {
chomp;
say "$.: $_";
}
close $in;
my $slurp = do {
local $/ = undef;
open(my $fh, '<', \$text) or die $!;
<$fh>;
};
say "slurped ", length $slurp, " characters";
my @paras = do {
local $/ = '';
open(my $fh, '<', \$text) or die $!;
<$fh>;
};
say "paragraphs: ", scalar @paras;
The text is read through an in-memory filehandle, so no external file is needed.
The loop prints each line prefixed with $.. The do
blocks localize $/, so the change does not leak out: with
undef the single read returns all 25 characters, and with an empty
string the text is split into two paragraphs separated by the blank line.
$ perl input_vars.pl 1: line1 2: line2 3: 4: line4 5: line5 slurped 25 characters paragraphs: 2
Best Practices
- Use local when changing special variables:
local $/orlocal $,in a small block restores the old value and protects other code. - Copy capture variables early:
$1and the other match variables are overwritten by the next successful match. - Check $! right after the failing call: any later system call can overwrite it.
- Use a named variable for readability: prefer
for my $item (@list)over$_when the body is longer than a line or two.
In this article we have covered the most common Perl special variables:
$_, $0, @ARGV, %ENV,
$!, $@, the output and process variables, the regex
match variables, and $. with $/.
Source
Author
List all Perl tutorials.