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
use strict;
use warnings;
print "script: $0\n";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:
| Variable | Holds | Set by |
|---|---|---|
$! | the last error from the operating system | a failed file or system call — Module 13 |
$@ | the last error Perl itself trapped | a trapped fatal error — Chapter 14.4 |
$? | the exit status of a program your script ran | running 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
open(my $fh, "<", "/no/such/file") or print "errno: $!\n";errno: No such file or directoryYou 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
eval { die "something failed\n" };
print "trapped: $@";trapped: something failedeval { ... } 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
system("/bin/sh", "-c", "exit 3");
printf "raw=%d code=%d\n", $?, $? >> 8;raw=768 code=3Two 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 why768becomes3when shifted right by eight. Module 15 explains the rest of what is packed in there.$? == -1means 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:
use strict;
use warnings;
for ("alu_smoke") {
print "default: $_\n";
}default: alu_smokeA 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
- Perl predefines variables with punctuation names; five are common enough to recognise immediately.
$0is the name the script was invoked as.$!holds the last operating-system error,$@the last error Perl trapped,$?the exit status of a program your script ran.- Those three are not interchangeable — reporting the wrong one blames the wrong thing.
$?carries the exit code in its high byte, so it is shifted right by eight;-1means the command never ran.$_is the variable Perl uses when you name none, and it is why some short Perl is hard to read.- 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.
$0is 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-1means 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.
