Skip to content
VLSI Mentor

awk Part II · Module 5 · Variables, Operators, and Control Flow

next and exit

Every rule so far ran to completion. This chapter is about stopping early — abandoning the current record with next, or ending the whole run with exit. Both are simple and both have a consequence that is not: next skips the remaining rules, which is what makes a chain of filters mutually exclusive, and exit stops reading input while still running END, which means a summary written in END can report a partial count in a sentence that reads exactly like a finished one. The chapter closes on the exit status, and on the reason a script that prints the word FAIL four times still tells the shell that everything succeeded.

Intermediate16 min readawkPart IIControl FlowExit StatusAutomation

Part II · Module 5 · Chapter 5.6 · next and exit

1. Why This Matters

Chapter 5.5 covered break and continue, which control a loop. This chapter covers the two statements that control record processing, which is a different scope and the source of most of the confusion:

  • next abandons the current record and moves to the next one.
  • exit stops reading input altogether and goes to END.

Both earn their place in this curriculum for reasons beyond syntax. next is what made Chapter 5.4's three-category classifier mutually exclusive rather than merely careful. And exit interacts with END in a way that can turn a correct summary into a confidently wrong one — section 8 — which is the most consequential thing in the chapter.

The last section is about the exit status, which is how awk talks to the script that called it, and which almost nothing you print has any effect on.

2. next — Abandon This Record

next stops processing the current record and fetches the following one:

Azvya Education Pvt. Ltd.VLSI Mentor
results.txt — synthetic teaching data. A test name and its result.
alu PASS
fifo FAIL
uart PASS
spi FAIL
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "PASS" { next } { print "failing:", $1 }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
failing: fifo
failing: spi

Reading it: for a passing record the first rule matches and next fires, so the second rule is never reached. For a failing record the first rule does not match, so execution falls through to the second.

The effect is "skip the records I do not care about", and it reads better than the negated condition in many cases — especially when there are several reasons to skip.

Two independent paths shown as two rows. The upper row: next appears in a rule, the remaining rules for that record are skipped, and awk reads the following record and carries on. The lower row: exit appears in a rule, input stops immediately with no further records read, and control passes to the END rule after which awk terminates.next in a rulethis record's turn endsLater rules skippedfor this record onlyRead the next recordthe run continuesexit in a rulethe reading endsNo more input readrecords left unseenEND runs, then stopwith a partial NR12
Figure 1 — next and exit act at different scopes, which is why they are drawn as two separate paths rather than as branches of one. next ends the current record's turn and awk carries on reading; exit ends the reading itself, and control goes straight to END. Nothing in the upper path can stop the run, and nothing in the lower path resumes it.

3. What next Actually Skips

The precise meaning is "skip the remaining rules for this record", and a three-rule program makes it visible:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print "rule1:", $1 } $2 == "PASS" { next } { print "rule3 (failures only):", $1 }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
rule1: alu
rule1: fifo
rule3 (failures only): fifo
rule1: uart
rule1: spi
rule3 (failures only): spi

Rule 1 ran for all four records — it comes before the next. Rule 3 ran only for the two failing ones.

So next does not skip the record entirely; it skips whatever is below it in the program. Rules already executed have already had their effect, which matters when one of them incremented a counter.

This is Chapter 2.3's source-order model again, and next is the one statement that interrupts it.

There is also nextfile, which abandons the rest of the current file and moves to the next one on the command line. This awk supports it, it is genuinely useful for "check the first record of each file and move on", and it is not in POSIX — so a script using it is not portable, and this chapter does not build on it.

4. The Skip-Comments Idiom

The most common real use of next is discarding lines that are not data. Tool reports are full of them:

Azvya Education Pvt. Ltd.VLSI Mentor
messy.txt — synthetic. A comment line, a blank line, and two data records.
# comment
alu PASS

fifo FAIL
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^#/ { next } NF == 0 { next } { print NR, $0 }' messy.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 alu PASS
4 fifo FAIL

Two guard rules, each ending in next, then the real work. The record numbers are 2 and 4, which confirms the skipped lines were read and counted — NR counts every record awk reads, not every record that survived the filters (Chapter 5.2).

That distinction is worth holding on to. A summary dividing by NR here would divide by 4, not 2 — the Chapter 5.3 denominator problem arriving through a different door. If you need a count of data records, count them:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^#/ { next } NF == 0 { next } { data++ } END { printf "%d data records of %d lines read\n", data+0, NR }' messy.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 data records of 4 lines read

Both numbers, so neither can be mistaken for the other.

The alternative to next is one compound condition:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '!/^#/ && NF > 0 { print NR, $0 }' messy.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 alu PASS
4 fifo FAIL

Identical output. The next version is preferable when the skip reasons are numerous or when each one needs its own counter — a report that says how many comment lines and how many blanks it discarded cannot be written as a single negated condition.

5. exit — Stop Reading

exit ends input processing immediately:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 2 { print "stopping at NR =", NR; exit }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
stopping at NR = 2

Records 3 and 4 were never read. Chapter 2.1 used exactly this to prove that awk reads one record at a time rather than slurping the file.

The use case is a search that can stop once it has an answer. On a 2 GB timing report, finding the first violation and stopping is a different operation from reading the whole file.

6. exit Still Runs END

This is the part that surprises people, and it is the foundation of section 8:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 2 { print "stopping at NR =", NR; exit } END { print "END ran, NR =", NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
stopping at NR = 2
END ran, NR = 2

END ran. And NR is 2 — the number of records read before the exit, not the 4 records in the file.

So exit means "stop reading and go to END", not "stop everything". That is a deliberate and useful design: it lets a script that stopped early still print a summary, close a report, or set a status.

It is also exactly how a correct-looking summary becomes wrong.

7. exit in BEGIN

The extreme case. exit in BEGIN skips input entirely, and END still runs:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { print "in BEGIN"; exit 7 } { print "read a record" } END { print "END ran, NR =", NR }' results.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
in BEGIN
END ran, NR = 0
rc=7

No record was read — the middle rule never produced a line — and END ran with NR at 0. The status 7 was preserved.

This is the natural place for a precondition check: validate arguments or environment in BEGIN, and exit with a status if something is missing. Just be aware that END will still run, so an END that prints a summary needs the NR == 0 guard Chapter 5.1 introduced, or it will print a summary of nothing.

8. The Partial Count

Now the danger, and it is the sharpest form of a problem this module keeps returning to.

A correct failure summary over the whole file:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { f++ } END { printf "%d failures of %d tests\n", f+0, NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 failures of 4 tests

Two failures in four tests. Now the same summary in a script that was changed to stop at the first failure — a reasonable-sounding optimisation:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { f++; exit } END { printf "%d failures of %d tests\n", f+0, NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 failures of 2 tests

1 failures of 2 tests. Both numbers are wrong. There are 2 failures, not 1, and 4 tests, not 2.

And the sentence is grammatically and structurally identical to the correct one. It names a numerator and a denominator, it is internally consistent — 1 really is the count of failures seen, and 2 really is the number of records read — and nothing in it indicates that the run stopped early. Pasted into a regression report, it is indistinguishable from a complete result.

9. What Actually Sets the Exit Status

The chapter's last idea, and it is the one that causes real incidents.

Here is a command reporting two failures:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { print "FAIL:", $1 }' results.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
FAIL: fifo
FAIL: spi
rc=0

Exit status 0. awk printed the word FAIL twice and told the shell that everything succeeded.

This is correct behaviour and it is worth being explicit about why: awk's exit status reports whether awk succeeded, not what awk found. It read its input, ran its program, and wrote its output, all without error. Status 0 is the truthful answer to that question. The content of stdout is not part of it.

On clean input the same command reports success:

Azvya Education Pvt. Ltd.VLSI Mentor
clean.txt — synthetic. Every test passed.
alu PASS
uart PASS
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { found = 1 } END { print "found =", found+0; exit (found ? 1 : 0) }' clean.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
found = 0
rc=0

Zero failures, status 0. The two runs are now distinguishable by a script, which is the whole point.

Choosing status values is worth a moment. By convention 0 means success and non-zero means a problem, and the specific value is yours to define — but 1 is also what many tools use for their own errors, so a distinct value makes a script's own failure distinguishable from the condition it was testing for. Chapter 5.4's sign-off summary used 3 for "unscored tests present" for exactly that reason.

10. exit Inside END

exit in END stops END and sets the status. It also overrides a status set earlier:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'END { print "in END"; exit 3 }' results.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
in END
rc=3

And with an earlier exit as well:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 1 { exit 5 } END { print "END ran" }' results.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
END ran
rc=5
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 1 { exit 5 } END { print "END ran"; exit 9 }' results.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
END ran
rc=9

The first command's exit 5 survived END, giving 5. The second's exit 9 inside END replaced it, giving 9.

So the status is whatever the last exit to execute specified. If a rule exits with a meaningful code and END then exits with its own, the rule's code is lost — which is a real bug in a script where END ends with an unconditional exit 0.

11. next Is Illegal in BEGIN and END

Unlike exit, next cannot appear in either phase:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'END { next }' results.txt 2>&1
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk: illegal break, continue, next or nextfile from END
 input record number 4, file results.txt
 source line number 1
rc=2
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { next }' results.txt 2>&1
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk: illegal break, continue, next or nextfile from BEGIN
 source line number 1
rc=2

A fatal error, status 2, with a clear message. The reason is structural: next means "go to the next record", and neither BEGIN nor END is processing a record, so the instruction is meaningless there.

This is a pleasant case — the error is loud and immediate, unlike most of what this module has covered.

12. break, continue, next, exit

The four-way comparison Chapter 5.5 promised. All four look like the same kind of statement and they act on three different scopes:

StatementScopeEffectLegal in BEGIN/END?
breakthe innermost loopleave the loop, continue the actionyes, inside a loop
continuethe innermost loopskip to the loop's next iterationyes, inside a loop
nextthe current recordskip the remaining rules, read the next recordno — fatal error
exit Nthe whole runstop reading, run END, set status Nyes

Reading the scope column top to bottom gives the mental model: loop, loop, record, run. A break inside a field loop does not abandon the record; a next inside a field loop abandons the record and therefore the loop as well.

That last point is worth demonstrating, since it is where the two get confused:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ for (i = 1; i <= NF; i++) { if ($i == "FAIL") next; print "  field", i, "=", $i }; print "finished record", NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  field 1 = alu
  field 2 = PASS
finished record 1
  field 1 = fifo
  field 1 = uart
  field 2 = PASS
finished record 3
  field 1 = spi

Records 1 and 3 printed a finished record line. Records 2 and 4 did not — next fired on their second field and abandoned the record, so the print "finished record" statement after the loop never ran.

With break instead, the record is not abandoned:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ for (i = 1; i <= NF; i++) { if ($i == "FAIL") break; print "  field", i, "=", $i }; print "finished record", NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  field 1 = alu
  field 2 = PASS
finished record 1
  field 1 = fifo
finished record 2
  field 1 = uart
  field 2 = PASS
finished record 3
  field 1 = spi
finished record 4

All four records report finished. break left the loop and execution continued with the next statement in the same action.

13. Common Mistakes

Expecting next to skip rules above it. It skips what comes after — section 3.

Dividing by NR after filtering with next. NR counts lines read, not records kept — section 4.

Printing a total in END after an early exit. The total is partial and reads as complete — section 8.

Expecting exit to skip END. It does not, in a rule or in BEGIN.

Expecting printed text to set the exit status. Only exit N does — section 9.

Ending END with an unconditional exit 0. It overrides a meaningful status set earlier — section 10.

Using next in BEGIN or END. A fatal error, status 2.

Confusing next with break inside a loop. break leaves the loop; next abandons the whole record and everything after the loop — section 12.

Using 1 as a "found it" status without thought. Many tools use 1 for their own errors; a distinct value is distinguishable.

14. Exercises

Exercise 1 — Predict the output

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 2 { next } { print NR, $1 }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 alu
3 uart
4 spi

Record 2 is skipped. Note NR still counts it — the output jumps from 1 to 3, which is the evidence that the record was read and discarded rather than never seen.

Exercise 2 — Skip non-data lines

Print only the data records of the messy file, with a count of each kind of line that was discarded.

Azvya Education Pvt. Ltd.VLSI Mentor
messy.txt
# comment
alu PASS

fifo FAIL
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^#/ { comments++; next } NF == 0 { blanks++; next } { data++; print NR, $0 } END { printf "%d data, %d comments, %d blanks, %d lines total\n", data+0, comments+0, blanks+0, NR }' messy.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 alu PASS
4 fifo FAIL
2 data, 1 comments, 1 blanks, 4 lines total

The three categories sum to 4, which is the arithmetic check that no line was silently lost — the same self-auditing shape as Chapter 5.4's three-category classifier.

Exercise 3 — exit and END

Predict both lines before running.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 1 { exit } END { print "END ran with NR =", NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
END ran with NR = 1

END runs despite the exit, and NR is 1 — the single record that had been read.

Exercise 4 — Debug a partial summary

A nightly job reports 1 failures of 2 tests for a file containing four tests, two of which failed. Find the bug.

Azvya Education Pvt. Ltd.VLSI Mentor
results.txt
alu PASS
fifo FAIL
uart PASS
spi FAIL
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { f++; exit } END { printf "%d failures of %d tests\n", f+0, NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 failures of 2 tests

The exit fires on the first failing record, so awk stops reading. f is 1 because one failure was seen and NR is 2 because two records were read. Both numbers are honest about what happened and both are wrong as a summary of the file.

Removing the exit restores the real answer:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { f++ } END { printf "%d failures of %d tests\n", f+0, NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 failures of 4 tests

The deeper lesson is that the broken output was plausible. If the file had happened to contain two tests with one failure, the two commands would agree and the bug would be invisible until the data changed.

Exercise 5 — Make the exit status meaningful

Write a check that a calling shell script can act on: status 0 if every test passed, status 1 if any failed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { found = 1 } END { exit (found ? 1 : 0) }' results.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
rc=1
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { found = 1 } END { exit (found ? 1 : 0) }' clean.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
clean.txt
alu PASS
uart PASS
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
rc=0

Two different statuses from the same command on different data, which is what makes it usable in a conditional.

Contrast with the version that only prints:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { print "FAIL:", $1 }' results.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
FAIL: fifo
FAIL: spi
rc=0

Two failures reported and status 0. A CI step running this records success.

Exercise 6 — Engineering application

Produce a regression gate: a human-readable summary on stdout and a status a pipeline can branch on, distinguishing "all passed", "some failed", and "the file had no records at all".

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { f++ } END { if (NR == 0) { print "ERROR: no records read"; exit 2 } printf "%d failures of %d tests\n", f+0, NR; exit (f ? 1 : 0) }' results.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 failures of 4 tests
rc=1
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "FAIL" { f++ } END { if (NR == 0) { print "ERROR: no records read"; exit 2 } printf "%d failures of %d tests\n", f+0, NR; exit (f ? 1 : 0) }' clean.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0 failures of 2 tests
rc=0
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
: > empty.txt
awk '$2 == "FAIL" { f++ } END { if (NR == 0) { print "ERROR: no records read"; exit 2 } printf "%d failures of %d tests\n", f+0, NR; exit (f ? 1 : 0) }' empty.txt
echo "rc=$?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ERROR: no records read
rc=2

Three distinct statuses for three distinct situations. The third is the one most scripts omit, and it is the one that matters most — an empty results file means the regression did not run, which is a different problem from a regression that ran and failed, and a summary reading 0 failures of 0 tests with status 0 would report it as a clean pass.

Exercise 7 — Challenge: next against break in a loop

Predict both outputs, then explain the difference in terms of scope.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ for (i = 1; i <= NF; i++) { if ($i == "FAIL") next; print "  field", i, "=", $i }; print "finished record", NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  field 1 = alu
  field 2 = PASS
finished record 1
  field 1 = fifo
  field 1 = uart
  field 2 = PASS
finished record 3
  field 1 = spi
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ for (i = 1; i <= NF; i++) { if ($i == "FAIL") break; print "  field", i, "=", $i }; print "finished record", NR }' results.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  field 1 = alu
  field 2 = PASS
finished record 1
  field 1 = fifo
finished record 2
  field 1 = uart
  field 2 = PASS
finished record 3
  field 1 = spi
finished record 4

next abandons the record, so the print "finished record" after the loop is skipped too — only records 1 and 3 report finishing. break abandons only the loop, so execution continues with the next statement in the same action and all four records report.

The scope rule in one sentence: break ends the loop, next ends the record, exit ends the run. Choosing between them is choosing how much to abandon.

15. Summary

  • next abandons the current record and skips the remaining rules — rules above it have already run.
  • next is the idiom for discarding non-data lines, and it is what makes a chain of classification rules mutually exclusive.
  • NR counts lines read, not records kept. After filtering with next, it is the wrong denominator.
  • exit stops reading input and still runs END, with NR holding a partial count.
  • exit in BEGIN reads no input at all, and END still runs with NR at 0.
  • An early exit invalidates every total in END — and 1 failures of 2 tests reads exactly like a complete summary.
  • Nothing you print affects the exit status. Printing FAIL leaves it at 0, which is how a broken regression passes a CI gate.
  • Only exit N sets the status, and the last exit to run decides it — so an unconditional exit 0 in END discards a meaningful code set earlier.
  • next is a fatal error in BEGIN or END, status 2.
  • break ends the loop, next ends the record, exit ends the run. Three scopes, four statements.

That completes Module 5, and with it the control flow of the language. You can hold state across records, read what awk reports about the input, compute reliably and say how much data the computation had, branch on a condition without being fooled by a malformed field, repeat over a record of unknown width, and stop early — telling the calling script what happened.

Every accumulator so far has been a single variable, which means a per-category total needs a separate variable per category, named in advance. Module 6 — Arrays, Functions, and Aggregation removes that limit: one array indexed by any string at all, which is how awk counts occurrences of values it has never seen, groups records by a field, and produces the per-instance and per-corner breakdowns that col[i] in Chapter 5.5 only hinted at. It closes with functions of your own, where awk has rules that surprise everyone: arrays pass by reference, scalars by value, and the only local variables you get are extra parameters you declare and never pass.

Where this fits

Part of the awk curriculum.