Skip to content
VLSI Mentor

Perl Part I · Module 1 · Introduction to Perl

perl -c: Checking a Script Without Running It

Chapter 1.5 showed that Perl compiles a whole file before running any of it. perl -c stops after that first stage and reports the result, which makes it the cheapest useful check in the language: it finds every syntax fault in a file in milliseconds, including faults in code that only runs once a month. This chapter covers what it catches, the two things it provably cannot catch, and a caveat most introductions omit — -c is not completely inert, because use statements and BEGIN blocks are part of compiling and therefore actually execute.

Foundation12 min readPerlPart IFundamentalsperl -cVerification

Part I · Module 1 · Chapter 1.13 · perl -c: Checking a Script Without Running It

1. Stage One on Demand

-c tells Perl to compile the file and stop — exactly the first of the two stages from Chapter 1.5:

Azvya Education Pvt. Ltd.VLSI Mentor
Check a script without running it
perl -c check.pl
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
check.pl syntax OK

That is the whole interface. It exits 0 when the file compiles. On a fault it reports the same message you would get from running the file, plus a slightly different summary line:

Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — a file with a missing semicolon
syntax error at compile-fault.pl line 5, near "print"
compile-fault.pl had compilation errors.

and exits 255. Note the wording differs from a normal run, which says Execution of ... aborted due to compilation errors. Under -c there was never going to be an execution, so Perl says had compilation errors instead.

2. Why This Is Worth a Habit

The value is not that it finds faults a run would miss — a run finds the same ones. The value is when it finds them, and which ones.

It is effectively instant. A script that takes two hours to reach its reporting step can be syntax-checked in milliseconds. If the fault is a typo in that reporting step, -c tells you now rather than in two hours.

It checks code that does not run on this execution. This is the real argument. A script has branches — the part that handles an error, the part that runs only with a particular option, the part for the once-a-quarter case. Running the script exercises one path through it. Compiling it checks the syntax of every line, including the ones today's run will never touch.

That second property is why a typo in an error-handling branch is such a classic failure: it is dormant until the bad night when the error branch finally runs, and then it fails while handling the first failure. perl -c would have found it the day it was written.

3. What It Cannot Do

Two limits, both provable, and both important enough that misunderstanding them makes -c dangerous rather than useless.

It cannot predict a runtime failure. Here is a program that is guaranteed to stop as soon as it runs:

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

print "first\n";
die "stopping here\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — perl -c dies.pl
dies.pl syntax OK

Exit 0. That verdict is correct: the file is a valid Perl program, and die is a legitimate instruction. -c answers "can this be compiled?" and nothing else. A clean -c is not a prediction that the script will work.

It cannot check your logic or your data. A script that compiles perfectly and computes the wrong number passes -c silently. So does one that will meet a missing input file. Neither pragma from Chapters 1.10 and 1.11 changes that either — use strict is checked by -c, but a declared variable holding a wrong value is not a syntax question.

4. What It Does Catch

The complement of §3, and it is a longer list than people expect, because -c performs the whole compile stage rather than a shallow scan:

  • Missing semicolons, unbalanced braces, unterminated strings — the whole family from Chapter 1.12.
  • Undeclared variables, when use strict is in effect. This is why §2's claim about untaken branches matters so much: a misspelled variable name anywhere in the file is caught, including in a branch that never runs.
  • A use that cannot be satisfied, because loading modules is part of compiling.
  • Features used without being enabled — the say failure from Chapter 1.6.

Between them those cover most of what actually goes wrong in a script's first week.

5. The Caveat: -c Is Not Inert

Most introductions say -c "checks without running your code". That is close enough to be useful and wrong in a way that occasionally matters, so this chapter states it exactly.

use statements and BEGIN blocks are part of compiling, so -c executes them. A BEGIN block is a block Perl runs while compiling rather than afterwards; you do not need to write one, but you will see them in real code and Module 12 covers them. Here is the proof:

Azvya Education Pvt. Ltd.VLSI Mentor
begin-demo.pl
BEGIN { print "BEGIN ran during compilation\n" }
print "body ran\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output — perl -c begin-demo.pl
begin-demo.pl syntax OK
BEGIN ran during compilation

The BEGIN block printed. The ordinary statement on line 2 did not. Running the same file normally gives both lines.

Two practical consequences:

  • -c on an unfamiliar script is not guaranteed to be side-effect free. A BEGIN block, or a module whose loading does work, will execute. On a script you did not write and have not read, "just syntax-check it" is a slightly stronger action than it sounds.
  • This is why -c fails on a missing module. The use is executed, cannot be satisfied, and reports BEGIN failed--compilation aborted — the message from Chapter 1.5. That is a correct compile-stage failure, not -c overreaching.

6. Using the Exit Status

-c reports its verdict twice: in words, and in its exit status. Zero means it compiled; non-zero means it did not. The status is the half a program can act on, which is what makes -c usable as a gate rather than only as a thing you read.

That is the mechanism behind a pre-merge check: run perl -c over every script in a change, and refuse the change if any of them fails. Module 15 covers exit statuses properly and Chapter 27.11 covers the review discipline; the point here is simply that the information is available in a form automation can use.

7. Mistakes and How They Look

Treating syntax OK as "it works". The most consequential misreading, and §3 is the counter-example. A clean check means the file is a valid program, not a correct one.

Checking the wrong file. perl -c on a path that does not exist gives Can't open perl script, the same message as Chapter 1.3 — Perl was never given a file. Easy to do when you are checking several at once.

Forgetting that use strict participates. A script without the pragma gets a much weaker check from -c, because undeclared variables are no longer errors. The two tools compound: use strict plus perl -c catches typos anywhere in the file; neither alone does.

Assuming -c is read-only on any script. As §5 showed, BEGIN blocks and module loading execute.

8. Why This Matters in Engineering Work

The economics are stark, and they are the whole reason this chapter exists in a beginner module.

A flow script's expensive paths are the rare ones. The code that handles a tool crash, a truncated log or an unexpected format runs on the bad night — which is precisely the night you cannot afford a second failure. A syntax fault in that code is invisible to every successful run and to every test that does not deliberately provoke the failure.

perl -c costs milliseconds and checks all of it. That makes it the highest-value-per-second check available on a Perl codebase, and the reason it belongs in a pre-merge gate rather than in a developer's memory. Part II builds on that with real tests, which go further: they check behaviour, not just syntax. -c is the floor, not the ceiling.

9. What You Should Be Able to Say Now

  1. perl -c performs the compile stage and stops, exiting 0 on success and non-zero on a fault.
  2. Its unique value is checking the syntax of lines that this run would never execute.
  3. It cannot predict a runtime failure — a script that always dies still passes.
  4. It cannot check logic or data; a wrong answer is not a syntax question.
  5. It catches undeclared variables only when use strict is in effect, so the two tools compound.
  6. It is not inert: use and BEGIN are part of compiling and therefore execute.

10. Exercise

Exercise 1 — the untaken branch. Write a script with a print and, below it, a deliberately misspelled variable name under use strict. Run the script and then perl -c it. Both should fail; explain why -c found a fault in a line that execution would also have reached, and then say how the result would differ if the typo were inside a branch that today's run skipped.

Exercise 2 — the limit. Run perl -c on dies.pl from §3 and record the verdict and exit status. Then run the script. Write one sentence explaining why -c's answer was correct rather than wrong.

Exercise 3 — prove the caveat. Create begin-demo.pl from §5 and run perl -c on it. Record exactly which of the two lines printed, and explain the result in terms of the compile stage.

Exercise 4 — build a gate. Describe, in two or three sentences, how you would use perl -c and its exit status to stop a broken script being committed. State one class of fault your gate would catch and one it would not.

11. Summary

  • perl -c is stage one on demand: syntax OK and exit 0, or the fault and a non-zero status.
  • Its real value is coverage, not speed — it checks every line, including the branches this run will not take.
  • A clean check is not a prediction. A script that dies immediately still reports syntax OK, correctly.
  • It catches undeclared variables only with use strict, so the pragma and the check are worth more together than apart.
  • It is not inert. use and BEGIN execute, which is also why a missing module makes -c fail.
  • The exit status makes it automatable, which is what turns a habit into a gate.

One chapter remains. Chapter 1.14 covers where to look when none of this module's messages tells you what you need — the documentation that shipped with the interpreter you found in Chapter 1.2.

Where this fits

Part of the Perl curriculum.