Skip to content
VLSI Mentor

Perl Part I · Module 2 · Variables, Scalars, and Data Types

The Special Variables Worth Knowing: $_, $0, $!, $@, $?

Perl has a set of variables whose names are punctuation rather than words, and you will meet several of them in the first inherited script you open. Five are common enough to learn now, and three of those five hold error information — from the operating system, from Perl itself, and from a program your script ran. They are not interchangeable, and a script that reports the wrong one blames the wrong thing, which is a particularly unhelpful kind of bug. This chapter says exactly what each holds, shows each one carrying a real value, and leaves the machinery that produces them to the modules that own it.

Foundation12 min readPerlPart IFundamentalsSpecial VariablesDebugging

Part I · Module 2 · Chapter 2.12 · The Special Variables Worth Knowing: $_, $0, $!, $@, $?

1. Why Punctuation Names Exist

Perl predefines a number of variables with names made of punctuation. They look cryptic, and Chapter 1.9 used them as an argument for commenting: a reader who has not learned them cannot guess what a line does.

The reason they exist is that each one is wanted constantly and in short code. $! carries the last operating-system error, and in a one-liner that fits where $last_os_error_message would not. Whether that trade was worth making is a fair question; the practical position is that they are in the language and in every script you will inherit, so the five most common are worth knowing by sight.

This chapter introduces them. It does not teach the machinery that fills them — each is produced by something covered in a later module, and this chapter uses the minimum needed to make the value visible.

2. $0 — the Script's Own Name

Azvya Education Pvt. Ltd.VLSI Mentor
name.pl
use strict;
use warnings;

print "script: $0\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
script: name.pl

$0 holds the name the script was invoked as. It is the usual way a script identifies itself in its own messages — a usage line, for instance, as Chapter 1.3 hinted when discussing how a script is run.

Note it reports how the script was invoked, so running it by a full path puts that path in $0. That is what you want in an error message: it tells you which file actually ran.

3. The Three Error Variables

This is the part worth real attention. All three hold something about a failure, and they hold different failures:

VariableHoldsSet by
$!the last error from the operating systema failed file or system call — Module 13
$@the last error Perl itself trappeda trapped fatal error — Chapter 14.4
$?the exit status of a program your script ranrunning an external command — Module 15

Put plainly: $! is the system complaining, $@ is Perl complaining, and $? is another program reporting how it finished. A script that prints $! when the real failure was a trapped Perl error will report something stale and irrelevant, which is worse than reporting nothing.

$! — the operating system

Azvya Education Pvt. Ltd.VLSI Mentor
os-error.pl — open belongs to Module 13
open(my $fh, "<", "/no/such/file") or print "errno: $!\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
errno: No such file or directory

You met this in Chapter 1.3 and again in Module 1's error discussion: the or die "...: $!" habit exists so that a failed file operation says why it failed rather than merely that it did.

$@ — Perl's own trapped error

Azvya Education Pvt. Ltd.VLSI Mentor
perl-error.pl — eval belongs to Chapter 14.4
eval { die "something failed\n" };
print "trapped: $@";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
trapped: something failed

eval { ... } runs a block and catches a fatal error instead of letting it stop the program. When it catches one, the message lands in $@. Chapter 14.4 covers it and the traps around it.

$? — another program's exit status

Azvya Education Pvt. Ltd.VLSI Mentor
child-status.pl — system belongs to Module 15
system("/bin/sh", "-c", "exit 3");
printf "raw=%d  code=%d\n", $?, $? >> 8;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
raw=768  code=3

Two things to carry away, both of which cost real time to discover:

  • $? is not the exit code directly. The code sits in the high byte, which is why 768 becomes 3 when shifted right by eight. Module 15 explains the rest of what is packed in there.
  • $? == -1 means the command never ran at all — Perl could not start it. That is a different failure from a command that ran and reported an error, and treating them alike will send you looking for a bug in a program that was never executed.

4. $_ — the Default Variable

$_ is the variable Perl uses when you do not name one. Chapter 1.9 mentioned it; here it is carrying a value:

Azvya Education Pvt. Ltd.VLSI Mentor
default-var.pl
use strict;
use warnings;

for ("alu_smoke") {
    print "default: $_\n";
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
default: alu_smoke

A loop that does not name a loop variable puts each value in $_. Several other constructs do the same, and Module 9 covers them properly — the one-item loop here exists only to make the variable observable without teaching loop mechanics.

$_ is what makes Perl's short forms short, and it is also why some Perl is hard to read: a line can operate on a value whose name never appears. Chapter 9.7 is devoted to it.

5. Why This Matters in Engineering Work

Two concrete payoffs.

Diagnosing an inherited script. When a legacy flow script reports a failure, the first useful question is often which failure it reported. A script printing $! after a step that actually failed inside Perl is telling you about the last unrelated file operation — a message that is real, stale and misleading. Knowing which variable holds which kind of error lets you read such a report sceptically.

Writing a report worth receiving. The same distinction applies when you write the message. "Could not open the log: No such file or directory" is actionable. "Could not open the log" is not, and "Could not open the log: Inappropriate ioctl for device" means you printed a stale $! after something that was not a file operation. The habit is to print the variable that belongs to the thing that just failed, and to print it immediately — these variables are overwritten by the next operation of their kind.

That last point is the one beginners miss most: $! and $@ are not records of what went wrong in your script. They hold the most recent value, and a line of cleanup between the failure and the message can replace it.

6. Common Mistakes

Using $! for a Perl error. The system did not fail; Perl did. $@ is the one that holds it.

Checking $@ without having used eval. Nothing trapped an error, so $@ holds whatever it held before — usually nothing.

Reading $? as the exit code. Shift it right by eight, and treat -1 as "never ran".

Letting the value go stale. Print or save it on the line after the failure. Any intervening operation of the same kind can overwrite it.

Assuming $0 is the file's path on disk. It is how the script was invoked, which may be a bare name or a full path.

Relying on $_ across a lot of code. It is overwritten by the next construct that uses it, and a reader cannot see what it holds. Name your variables when the code is more than a line or two.

7. What You Should Be Able to Say Now

  1. Perl predefines variables with punctuation names; five are common enough to recognise immediately.
  2. $0 is the name the script was invoked as.
  3. $! holds the last operating-system error, $@ the last error Perl trapped, $? the exit status of a program your script ran.
  4. Those three are not interchangeable — reporting the wrong one blames the wrong thing.
  5. $? carries the exit code in its high byte, so it is shifted right by eight; -1 means the command never ran.
  6. $_ is the variable Perl uses when you name none, and it is why some short Perl is hard to read.
  7. All of these hold the most recent value, so use them immediately after the operation you care about.

8. Exercise

Exercise 1 — identify your script. Write a script that prints its own name using $0. Run it twice: once as perl name.pl and once with the full path to the file. Record both outputs and explain the difference.

Exercise 2 — see two different errors. Write a script that first attempts to open a file that does not exist and prints $!, then uses eval { die "manufactured\n" } and prints $@. Confirm the two messages are different and say which component produced each.

Exercise 3 — decode an exit status. Using the list form from §3, run a command that exits with status 5 and print both $? and $? >> 8. Verify the relationship between the two numbers, and state what you would conclude if $? were -1.

Exercise 4 — spot the stale value. A colleague's script does a file operation, then prints a progress message, then prints "failed: $!". Explain why the reported reason may be misleading, and give the one-line change that fixes it.

9. Summary

  • Punctuation-named variables are short because they are used constantly — and are why some Perl needs comments to be readable.
  • $0 is the name the script was invoked as.
  • Three variables hold three different failures: $! from the operating system, $@ from Perl, $? from a program you ran.
  • $? needs shifting — the exit code is in the high byte — and -1 means the command never started.
  • $_ is the unnamed default, which makes short code short and long code opaque.
  • They all hold the latest value, so read them immediately after the operation in question.

Chapter 2.13 closes the module by returning to a warning you first met in Module 1, and turning it from an annoyance into the most useful signal your parsing scripts will have.

Where this fits

Part of the Perl curriculum.