Skip to content
VLSI Mentor

awk Part I · Module 4 · Patterns, Conditions, and Regular Expressions

Conditional Filtering

Chapter 4.1 showed where a pattern looks and Chapter 4.2 gave you the operators to combine several tests. Both were about getting one condition right. This chapter is about a decision neither of them raises, and it changes what your script is able to report. A condition can live in the pattern, where it selects which records the action runs on, or inside the action, where every record arrives and the condition decides what to do with it. Those are not two spellings of the same thing: a pattern filter discards the records it rejects, so nothing later in the program can count them. That is the difference between reporting two violations and reporting two violations out of four paths, and only one of those is a number anybody should act on.

Foundation16 min readawkPart IPatternsFilteringEDA Reports

Part I · Module 4 · Chapter 4.6 · Conditional Filtering

1. Why This Matters

You can now write any condition you like. What this chapter adds is where to put it.

A condition in the pattern decides which records the action sees. A condition inside the action sees every record and decides what to do with each one.

Both are correct awk. They produce different programs, and the difference shows up not in the filtered output but in what you can say about it.

The report for this chapter:

Azvya Education Pvt. Ltd.VLSI Mentor
timing.rpt — synthetic teaching data. Endpoint, check type, slack, verdict.
u_core/u_alu setup -0.124 VIOLATED
u_core/u_regfile setup 0.081 MET
u_pad/u_io hold -0.052 VIOLATED
u_pad/u_clk hold 0.410 MET

2. The Rule You Actually Want

A real selection rule usually combines a text test with a numeric one, because the two check different things. Here is the registry's own example for this chapter:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/VIOLATED/ && $3 < 0 { print $1, $3 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu -0.124
u_pad/u_io -0.052

Two conditions: the verdict says the tool thinks this failed, and the slack says the arithmetic agrees. On a well-formed report they always agree, which makes the conjunction look redundant.

It is not redundant — it is a cross-check. Chapter 1.2 drew the distinction between a recorded result and a measurement, and this rule demands both. A record where the tool said VIOLATED and the slack is positive would be excluded, and that exclusion is information: it means the report is internally inconsistent and worth looking at by hand.

Writing it with the tighter forms from Chapter 4.1 and the forced arithmetic from Chapter 4.2:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$4 == "VIOLATED" && $3 + 0 < 0 { print $1, $3 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu -0.124
u_pad/u_io -0.052

Same answer, and now the verdict is anchored to its column and the comparison cannot silently become textual.

3. Filtering in the Pattern

The form you have used throughout. The condition goes before the braces and the action runs only on matching records:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$3 + 0 < -0.1 { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu

One record is worse than -0.1. The other three were read, split into fields, tested, and discarded.

This is the right default. It is short, it reads as a sentence, and the output contains exactly what you asked for.

4. Filtering in the Action

The same selection, with the condition moved inside:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ if ($3 + 0 < -0.1) print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu

Identical output. The rule now has no pattern, so it runs on every record, and an if inside decides whether to print. (if is Chapter 5.4's subject; it appears here because this chapter cannot make its point without it.)

So far this looks like a pointless extra four characters. The difference appears the moment the program needs to say anything about the records it did not select.

5. The Difference: What END Can See

Count the violations with a pattern filter:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$3 + 0 < 0 { n++ } END { print "pattern selected", n }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
pattern selected 2

Two. Now the same count with the filter inside the action, keeping a second counter for everything seen:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ if ($3 + 0 < 0) n++; total++ } END { print "action saw", total, "selected", n }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
action saw 4 selected 2

Two out of four.

condition in the patterncondition in the action
Runs onmatching records onlyevery record
Outputonly selected recordswhatever you choose per record
Can count what was rejectednoyes
Can report a subset denominatornoyes
Can label every recordnoyes
Lengthshortera few characters more
Reach for it whenyou want the matching rowsyou want to say something about the whole file

6. Labelling Instead of Dropping

Once every record reaches the action, you can classify rather than discard. The ternary operator does it in one expression — condition ? value-if-true : value-if-false:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, ($3 + 0 < 0 ? "FAIL" : "ok") }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu FAIL
u_core/u_regfile ok
u_pad/u_io FAIL
u_pad/u_clk ok

Four input records, four output rows, each carrying a verdict this script derived itself.

No pattern filter can produce that table, because the passing rows would never reach an action. And a table with one row per test is exactly what a regression summary or a per-path review wants — the absent rows in a filtered list are indistinguishable from tests that were never run, which Chapter 1.2 warned about from the other direction.

7. Layering Conditions

Real rules accumulate conditions. Each one narrows the selection, and && is the join:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "setup" && $3 + 0 < 0 && $4 == "VIOLATED" { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu

Three tests: the check type, the arithmetic, and the tool's verdict. One record satisfies all three.

Order them with Chapter 4.2's short-circuit rule in mind — the cheapest and most selective test first. $2 == "setup" is a single field comparison that eliminates half the file, so it belongs at the front; a regex over the whole record belongs at the back.

And remember Chapter 4.2's parenthesis habit the moment an || appears:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '($2 == "setup" || $2 == "hold") && $3 + 0 < 0 { print $1, $2 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu setup
u_pad/u_io hold

8. A Threshold From Outside the Script

A rule containing a hard-coded number is a rule that needs editing to be reused. -v passes one in:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -v lim=-0.1 '$3 + 0 < lim { print $1, $3 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu -0.124

The same command answers a different question by changing one word on the command line, which is what makes it usable from a shell script. Chapter 8.2 covers -v properly, including why the value arrives as text and why the + 0 on the field is still worth having.

9. Counting Each Category in One Pass

Several rules over one file, each counting its own category, with the totals in END:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$4 == "VIOLATED" { v++ } $4 == "MET" { m++ } END { print "violated", v+0, "met", m+0 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
violated 2 met 2

Two rules fire on each record — at most one of the category rules, then nothing else — and the counts sum to 4, which is NR. That sum is the cross-check: if it did not equal NR, some record has a verdict that is neither word, and the categories are not exhaustive.

Making that check explicit rather than mental:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$4 == "VIOLATED" { v++; next } $4 == "MET" { m++; next } { other++ } END { print "violated", v+0, "met", m+0, "unclassified", other+0, "of", NR }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
violated 2 met 2 unclassified 0 of 4

The next calls make the rules exclusive — Chapter 2.3's lesson — and the catch-all rule counts anything the named categories missed. unclassified 0 of 4 is a stronger statement than violated 2 met 2, because it proves nothing was silently skipped.

10. An Engineering Example

A wider report, and the two shapes of question a timing review actually asks.

Azvya Education Pvt. Ltd.VLSI Mentor
timing_full.rpt — synthetic teaching data. Endpoint, check, clock, slack, verdict.
u_core/u_alu setup clk_main -0.124 VIOLATED
u_core/u_regfile setup clk_main 0.081 MET
u_pad/u_io hold clk_io -0.052 VIOLATED
u_pad/u_clk hold clk_io 0.410 MET
u_core/u_mul setup clk_main -0.310 VIOLATED

The filtered list — which paths need work, sorted by nothing, just listed:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -v lim=-0.1 '$5 == "VIOLATED" && $4 + 0 < lim { print $1, $3, $4 }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu clk_main -0.124
u_core/u_mul clk_main -0.310

A pattern filter is right here. The question is "which rows", the answer is those rows, and nothing is being claimed about the rest.

The summary — which needs the denominator, so the filter moves inside:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ total++; if ($5 == "VIOLATED") { v++; if ($4 + 0 < worst) { worst = $4 + 0; wp = $1 } } } END { printf "%d of %d checks violated; worst %s at %s\n", v+0, total, worst+0, (wp == "" ? "none" : wp) }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3 of 5 checks violated; worst -0.31 at u_core/u_mul

Every record reached the action. total counted all of them, v counted the violations, and the worst case was tracked only among the violations. The output is a claim with a denominator attached, which is the form a report can carry.

Note worst -0.31 rather than -0.310 — the + 0 made it a number and a trailing zero is not part of a number's value, which Chapter 4.5 met as well. And the wp == "" guard produces none rather than an empty space on a clean report, which is Chapter 2.4's empty-case discipline.

The caveats are unchanged and are the reason the denominator matters. VIOLATED is the tool's own verdict, not an independent check. These five rows are checks, so a path appearing under both setup and hold counts twice — which is exactly why 3 of 5 checks is the honest phrasing and 3 violations is not.

11. Common Mistakes

Filtering in the pattern and then wanting a denominator. Section 5. The rejected records are gone.

Reporting a count without its total. 2 against 2 of 4. Chapter 1.2's rule, and section 5 is the mechanism.

Using a ternary where the branches do different things. Section 6's callout. It chooses between values, not actions.

Putting the expensive test first in a chain. Chapter 4.2's short-circuit rule — order the cheap, selective test to the left.

Mixing && and || without parentheses. Section 7, and Chapter 4.2 section 6 for why it changes the answer.

Assuming two category rules are exhaustive. Section 9. Add a catch-all and prove the counts sum to NR.

Forgetting + 0 on a numeric field from a file you did not write. Chapter 4.2 section 3.

Producing a filtered list and calling it a per-test table. The absent rows look like tests that never ran. Section 6 is the fix.

12. Exercises

Exercise 1 — Predict the output

Azvya Education Pvt. Ltd.VLSI Mentor
timing.rpt
u_core/u_alu setup -0.124 VIOLATED
u_core/u_regfile setup 0.081 MET
u_pad/u_io hold -0.052 VIOLATED
u_pad/u_clk hold 0.410 MET
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$4 == "MET" && $3 + 0 > 0.2 { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_pad/u_clk

One record: MET and comfortably positive. u_core/u_regfile is MET but its 0.081 is below the threshold.

Exercise 2 — Modify a command

Turn the filtered list below into a table with one row per record, labelling each as violation or clean.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, ($4 == "VIOLATED" ? "violation" : "clean") }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu violation
u_core/u_regfile clean
u_pad/u_io violation
u_pad/u_clk clean

The pattern was removed so every record reaches the action, and the ternary supplies the label. Four rows in, four rows out.

Exercise 3 — Trace what END can see

Predict both outputs, paying attention to which program can report a total.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$3 + 0 < 0 { n++ } END { print n+0, NR }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 4
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "setup" { s++ } $2 == "setup" && $3 + 0 < 0 { sv++ } END { print sv+0, "of", s+0, "setup checks" }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 of 2 setup checks

The first works because NR is available in END and the denominator happens to be the whole file. The second needs a subset denominator — setup checks only — and NR cannot supply it, so a second rule has to count them. That is section 5's limit: the pattern form is fine until the denominator stops being NR.

Exercise 4 — Debug a misleading summary

An engineer reports "4 violations" from the five-row report and a colleague says there are three. Their command is below. Explain the discrepancy.

Azvya Education Pvt. Ltd.VLSI Mentor
timing_full.rpt
u_core/u_alu setup clk_main -0.124 VIOLATED
u_core/u_regfile setup clk_main 0.081 MET
u_pad/u_io hold clk_io -0.052 VIOLATED
u_pad/u_clk hold clk_io 0.410 MET
u_core/u_mul setup clk_main -0.310 VIOLATED
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/VIOLATED/ { n++ } END { print n+0, "violations" }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3 violations

That command gives 3, so the engineer's 4 came from something else — most likely a whole-record match on a report where a path name contained the word, which is Chapter 4.1 section 4 exactly. The fix is to anchor the test and to report the denominator so the claim is checkable:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ total++ } $5 == "VIOLATED" { n++ } END { print n+0, "of", total, "checks violated" }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3 of 5 checks violated

Now a reader can verify the arithmetic against the file, and the word "checks" says what is being counted.

Exercise 5 — Engineering application

Report, for clk_main only, how many checks violated out of how many were analysed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$3 == "clk_main" { t++; if ($5 == "VIOLATED") v++ } END { print v+0, "of", t+0, "clk_main checks violated" }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 of 3 clk_main checks violated

The outer condition is in the pattern because it selects the population being described; the inner condition is in the action because both halves of the ratio are needed. That split — pattern for the population, action for the classification — is the practical shape of section 5's advice.

Exercise 6 — Challenge: one pass, three numbers, one verdict

Write a single command that reports the violation count, the total check count, the worst slack, and a one-word verdict, and that behaves sensibly on a report with no violations at all.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ total++ } $5 == "VIOLATED" { v++; if ($4 + 0 < worst) { worst = $4 + 0; wp = $1 } } END { printf "%d/%d violated, worst %s at %s, verdict %s\n", v+0, total, (v ? worst+0 : "n/a"), (v ? wp : "n/a"), (v ? "FAIL" : "CLEAN") }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3/5 violated, worst -0.31 at u_core/u_mul, verdict FAIL

And on a report where everything met its timing:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf 'u_a setup clk_main 0.100 MET\nu_b hold clk_io 0.200 MET\n' > clean.rpt
awk '{ total++ } $5 == "VIOLATED" { v++; if ($4 + 0 < worst) { worst = $4 + 0; wp = $1 } } END { printf "%d/%d violated, worst %s at %s, verdict %s\n", v+0, total, (v ? worst+0 : "n/a"), (v ? wp : "n/a"), (v ? "FAIL" : "CLEAN") }' clean.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0/2 violated, worst n/a at n/a, verdict CLEAN

Three design choices worth naming:

  • total is counted in a pattern-less rule, so it is a true denominator regardless of what else matched.
  • worst is only tracked inside the violation rule, so a clean report leaves it unset — and worst = 0 would have been a bug, since a slack of 0 is better than any violation and would have masked the real worst case.
  • Every output field is guarded by v, so a clean report prints n/a rather than an empty space or a misleading 0. Chapter 2.4 made this point about END running on empty input; here the input is fine and the category is empty, which is the same problem one level in.

The printf gives control over the layout; Chapter 7.2 covers it properly.

13. Summary

  • A condition in the pattern selects which records the action sees. A condition in the action sees every record and decides what to do with each.
  • A pattern filter discards what it rejects, so nothing later in the program can count it. 2 and 2 of 4 are different claims and only the second is reportable.
  • NR rescues the simple case but not a subset denominator — "2 of the 3 setup checks" needs its own counter, which only the action form can keep while filtering.
  • The ternary labels rather than drops, turning a filter into a classifier so every input record produces an output row. A filtered list is not a per-test table; its absent rows look like tests that never ran.
  • The ternary is an expression. Use it to choose a value, and if/else to choose an action.
  • Layer conditions with &&, cheapest and most selective first, and parenthesise the moment an || appears.
  • Pass thresholds in with -v so a rule is reusable without editing.
  • Add a catch-all when counting categories and prove the counts sum to NR.
  • Guard every reported value against its category being empty, or a clean report prints something misleading.

You can now write the selection rules a report script is actually made of, and choose a structure that can still report a denominator.

Every example in this module has run over one small file written to isolate one idea. The last chapter puts the whole module to work at once, over several files, in a single pass — which is what a nightly triage actually looks like. Chapter 4.7 — Filtering Real Logs and Reports is Module 4's capstone.

Where this fits

Part of the awk curriculum.