Skip to content
VLSI Mentor

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

Comparison and Logical Operators

Chapter 4.1 showed where a pattern looks. This chapter is about building a real selection rule once you have several things to test at once. Two parts of it are genuinely surprising. awk decides whether to compare as numbers or as text based on what is on both sides of the operator, so the same field can be greater than 9 and not greater than the quoted string nine — Chapter 1.2's unit-suffix trap was one symptom of a rule that is finally stated here. And the logical operators have a precedence that changes which records you select: one pair of parentheses turns a three-record answer into a two-record one, with no error either way.

Foundation16 min readawkPart IOperatorsComparisonsEDA Reports

Part I · Module 4 · Chapter 4.2 · Comparison and Logical Operators

1. Why This Matters

Chapter 4.1 ended with two rules that used && and || on trust. They are easy to use and easy to use wrongly, and both failure modes are silent.

Two facts carry this chapter:

awk decides numeric-or-text comparison from what is on both sides of the operator. And && binds tighter than ||.

Neither produces an error when you get it wrong. Both change which records you select.

2. The Six Comparison Operators

The usual set, and each yields 1 for true or 0 for false:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { a = 5; b = 9; print (a<b), (a<=b), (a>b), (a>=b), (a==b), (a!=b) }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 1 0 0 0 1
OperatorTrue when
<left is less than right
<=less than or equal
>greater than
>=greater than or equal
==equal
!=not equal

The fact that a comparison is a value — 1 or 0 — is why a bare comparison works as a pattern. $3 < 0 as a pattern means "run this rule when that expression is true", and truth in awk is any non-zero, non-empty value.

A synthetic timing report for the rest of the chapter:

Azvya Education Pvt. Ltd.VLSI Mentor
timing.rpt — synthetic teaching data. An endpoint, a check type, a slack, a 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
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$3 < 0 { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu
u_pad/u_io

3. What Counts as True

Section 2 ended by asserting that "truth in awk is any non-zero, non-empty value". That deserves explaining, because it has a sharp edge.

Azvya Education Pvt. Ltd.VLSI Mentor
truth.txt — synthetic. Field 2 is a number, a zero, missing, a decimal zero, and a word.
a 1
b 0
c
d 0.0
e text

A bare field used as a pattern asks "is this value true?":

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 { print NR, $1, "[" $2 "]" }' truth.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 a [1]
5 e [text]

Two records. And the complement:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '!$2 { print NR, $1, "[" $2 "]" }' truth.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 b [0]
3 c []
4 d [0.0]

So a field holding 0 is false, a missing field is false, and 0.0 is false too — but the word text is true.

The rule is that a value from the input is judged as a number if it looks like one, and as a string otherwise. 0 and 0.0 look numeric and are numerically zero, so they are false. text is not numeric, so it is judged as a string and a non-empty string is true.

The practical use of truthiness is as a guard. NF as a pattern selects records that have any fields at all, which is the tidiest way to skip blank records:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NF { print NR, $0 }' bl.txt
Azvya Education Pvt. Ltd.VLSI Mentor
bl.txt — synthetic. There is an empty line in the middle.
a 1

b 2
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 a 1
3 b 2

Record 2 is blank, so NF is 0, so the pattern is false. Chapter 2.2 introduced NF == 0 for this; the bare NF says the same thing more briefly.

An unset variable is false, which is why a half-written rule selects nothing:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { print (nope ? "true" : "false") }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
false

4. Numeric or Text? The Rule

This is the section to remember. Chapter 1.2 showed a comparison "silently changing kind" and Chapter 1.5 showed Python refusing to guess. Neither stated the rule, because stating it needs this chapter's vocabulary.

Here it is: awk compares numerically when both sides look like numbers, and as text otherwise. A field that looks numeric counts as a number; a quoted constant never does.

The demonstration. One file, two fields:

Azvya Education Pvt. Ltd.VLSI Mentor
nums.txt — synthetic. The first row is ten then nine.
10 9
9 10

Two numeric-looking fields compare numerically, as you would hope:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, ($1 > $2 ? ">" : "<="), $2 }' nums.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
10 > 9
9 <= 10

Now compare the same $1 against a numeric constant:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 1 { print ($1 > 9) ? "10 > 9 true" : "10 > 9 false" }' nums.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
10 > 9 true

And against a quoted constant:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 1 { print ($1 > "9") ? "10 > \"9\" true" : "10 > \"9\" false" }' nums.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
10 > "9" false

The other half of the rule: when the field itself is not numeric-looking, the comparison is textual however you write the other side:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, ($1 > $2 ? ">" : "<="), $2 }' units.txt
Azvya Education Pvt. Ltd.VLSI Mentor
units.txt — synthetic. The values carry a unit suffix.
10ns 9ns
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
10ns <= 9ns

10ns is "less than" 9ns as text, which as a statement about time is nonsense.

5. Forcing the Comparison You Meant

Two idioms, both already seen in passing and now explicable.

Force numeric by adding zero:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print ($1 + 0 > $2 + 0) ? "numeric: 10 > 9" : "numeric: not" }' units.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
numeric: 10 > 9

+ 0 converts each side to a number, discarding the suffix, so the comparison is arithmetic and correct.

Force text by concatenating an empty string:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 1 { print ($1 "" > $2 "") ? "string: 10 > 9" : "string: 10 <= 9" }' nums.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
string: 10 <= 9

$1 "" makes a string of it, so even two numeric-looking fields compare as text.

6. The Three Logical Operators

&& for and, || for or, ! for not.

Both conditions must hold:

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

Only one record is both a setup check and worse than -0.1. The other setup record has positive slack and the other negative-slack record is a hold check.

Either condition:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$4 == "VIOLATED" || $3 > 0.4 { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu
u_pad/u_io
u_pad/u_clk

The two violations plus the one comfortable path.

Negation, applied to a whole-record pattern:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '!/VIOLATED/ { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_regfile
u_pad/u_clk

7. Precedence: && Binds Tighter Than ||

Now the part that changes answers. These two commands differ by one pair of parentheses:

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

Three records against two. The first includes u_pad/u_clk, whose slack is a healthy 0.410.

The reason is precedence. && binds tighter than ||, so the unbracketed form groups like this:

Azvya Education Pvt. Ltd.VLSI Mentor
how awk reads the unbracketed version
$2 == "hold"   ||   ( $2 == "setup" && $3 < 0 )

Which is "every hold check, or any setup check with negative slack". Every hold record qualifies regardless of slack, which is why the comfortable u_pad/u_clk appeared.

The bracketed form says what the author almost certainly meant: "any check of either type, and negative slack".

8. Short-Circuit Evaluation

awk stops evaluating a logical expression as soon as the answer is settled. You can see it with an assignment as the side effect — if the right-hand side runs, n changes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { n = 0; if (0 && (n = 1)) ; print "n =", n }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
n = 0
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { n = 0; if (1 && (n = 1)) ; print "n =", n }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
n = 1
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { n = 0; if (1 || (n = 1)) ; print "n =", n }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
n = 0
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { n = 0; if (0 || (n = 1)) ; print "n =", n }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
n = 1
ExpressionRight side evaluated?Why
0 && …noa false left side settles &&
1 && …yesthe right side decides
1 || …noa true left side settles ||
0 || …yesthe right side decides

(The if here is Chapter 5.4's subject; it is used only to make the expression evaluate. The bare ; is an empty statement — there is nothing to do when the test is true, because the point is the side effect.)

This matters practically in one way: put the cheap, most-selective test first. $2 == "setup" && /some long pattern/ skips the pattern match entirely on every non-setup record. It is also why ordering a guard before the thing it guards works — NF == 5 && $4 + 0 < 0 will not evaluate $4 on a malformed record.

9. Chained Comparisons Are Rejected

In mathematics you can write one expression for "between". In awk you cannot, and the good news is that it is caught rather than misread:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { print (3 > 2 > 1) }'

awk refuses to parse this and exits with a non-zero status, reporting a syntax error at that line. The same is true of 1 < 2 < 3.

That is a kinder failure than it could have been — in some languages such an expression parses as (3 > 2) > 1, which is 1 > 1, which is false, and you get a wrong answer instead of a complaint. Here you get told.

The way to express a range is two comparisons joined by &&:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$3 > -0.1 && $3 < 0.1 { print $1, $3 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_regfile 0.081
u_pad/u_io -0.052

The two paths whose slack is within a tenth of a nanosecond of zero in either direction — the marginal ones, which are often the interesting set.

10. A Threshold From Outside the Script

A rule is more reusable when the number in it is not baked in. -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 with a different limit needs no edit. Note the + 0 on the field: lim arrives as text, and although -0.1 is numeric-looking and would compare numerically here, forcing the field is the habit from section 5 and costs nothing. Chapter 8.2 covers -v properly.

11. An Engineering Example

A selection rule a timing review would actually want: setup violations worse than a threshold, reported with their clock and slack.

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
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -v lim=-0.1 '$2 == "setup" && $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

Three conditions joined with && — an exact check type, an exact verdict, and a forced-numeric threshold. No parentheses are needed because there is no || in the rule, which is section 7's habit stated the other way round.

And the marginal set, where the mixture does need brackets:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '($2 == "setup" || $2 == "hold") && $4 + 0 < 0 { print $1, $2, $4 }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu setup -0.124
u_pad/u_io hold -0.052
u_core/u_mul setup -0.310

The caveats from Chapter 1.2 are unchanged and matter here more than usual, because this chapter is about building rules precise enough to act on. VIOLATED is the tool's own verdict rather than an independent check. A real report lists each physical path once per check and per corner, so counting these rows counts checks, not paths. And the threshold -0.1 is a judgement about what is worth looking at, not a property of the design.

12. Common Mistakes

Comparing a field against a quoted number. Section 4. $3 < "0" is a text comparison; $3 < 0 is arithmetic.

Trusting a numeric comparison on a field with a unit suffix. Section 4's second half, and Chapter 1.2's trap. Use + 0.

Mixing && and || without parentheses. Section 7. Three records where you meant two, silently.

Writing a chained comparison. Section 9. awk rejects it; use &&.

Using = instead of ==. $2 = "hold" assigns to field 2, rebuilds the record (Chapter 3.6) and is always true. awk will not stop you.

Putting the expensive test first. Section 8. Short-circuiting only helps if the selective test is on the left.

Assuming ! reaches across &&. !/a/ && /b/ is "(not a) and b", because ! binds tighter. If you meant "not (a and b)", bracket it.

Comparing a field to an unset variable. $1 == x where x was never set compares against the empty string, matches nothing, and reports no error — Chapter 1.6's quiet failure in a new place.

13. 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 '$3 > 0 && $4 == "MET" { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_regfile
u_pad/u_clk

The two records with positive slack and a MET verdict. On this file either condition alone would select the same two, which is worth noticing — the redundancy is a cheap cross-check that the verdict agrees with the arithmetic.

Exercise 2 — Modify a command

Select the records whose check is hold and whose slack is negative.

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

One record. The + 0 is not strictly needed on this clean file and is the habit section 5 argues for.

Exercise 3 — Trace the precedence

Work out by hand how awk groups the rule below, say which records it selects, then check.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$4 == "MET" || $4 == "VIOLATED" && $3 < -0.1 { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu
u_core/u_regfile
u_pad/u_clk

&& binds tighter, so awk reads it as $4 == "MET" || ($4 == "VIOLATED" && $3 < -0.1) — every MET record, plus violations worse than -0.1. That is three records including both passing paths, which is almost certainly not what someone writing this would have wanted.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '($4 == "MET" || $4 == "VIOLATED") && $3 < -0.1 { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu

With the brackets, one record. Same two conditions, same data, a different question.

Exercise 4 — Debug a comparison that selects nothing

An engineer filters a slack column and gets no output, though the report clearly has negative values. Their file and command are below. Diagnose it.

Azvya Education Pvt. Ltd.VLSI Mentor
slack_units.rpt — synthetic. The slack column carries a unit suffix.
path_a -0.124ns
path_b 0.081ns
path_c -1.200ns
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 < "0" { print $1 }' slack_units.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
path_a
path_c

That one does produce output — but by accident, and it is worth understanding why before trusting it. The field is not numeric-looking, and the constant is quoted, so this is a text comparison: -0.124ns sorts before "0" because - precedes 0 in the character order. It gives the right two rows on this data for the wrong reason, which is Chapter 1.2's warning exactly.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 + 0 < 0 { print $1, $2 }' slack_units.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
path_a -0.124ns
path_c -1.200ns

The forced-numeric version answers the question that was asked, and it keeps working on a value like +0.05ns where the text comparison would wrongly select.

Exercise 5 — Engineering application

From the five-column report, select every violation on clk_main, regardless of check type, and print the path and slack.

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 '$3 == "clk_main" && $5 == "VIOLATED" { print $1, $4 }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu -0.124
u_core/u_mul -0.310

Two exact field tests joined with &&. No ||, so no brackets needed.

Exercise 6 — Challenge: say what each rule asks

Three rules over timing_full.rpt. For each, state in English what it selects, predict the count, then check. One of them is a trap.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "setup" || $2 == "hold" && $4 + 0 < 0 { n++ } END { print n+0 }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
4
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '($2 == "setup" || $2 == "hold") && $4 + 0 < 0 { n++ } END { print n+0 }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$2 == "setup" && $4 + 0 < 0 || $2 == "hold" && $4 + 0 < 0 { n++ } END { print n+0 }' timing_full.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3

The first is the trap: "every setup check, or any hold check with negative slack". All three setup records qualify regardless of slack, plus the one negative hold — four. It reads like a range filter and is not one.

The second is "either check type, and negative slack" — three.

The third says the same thing as the second by distributing the && over both branches, and gets the same three. It is longer but needs no parentheses, which some people prefer; the bracketed form is shorter and, once you know the precedence, just as clear.

The lesson is that all three are valid awk and only one of them is wrong — and the wrong one is the one you get by typing what you meant without brackets.

14. Summary

  • A comparison is a value, 1 or 0, which is why a bare comparison works as a pattern.
  • awk compares numerically when both sides look like numbers, and as text otherwise. A numeric-looking field counts; a quoted constant never does.
  • The same field can be greater than 9 and not greater than "9". The quotes decide, and nothing warns you.
  • + 0 forces arithmetic; "" forces text. Use + 0 on any quantity in a file you did not write.
  • &&, || and ! — and && binds tighter than ||, so one pair of parentheses changed three selected records into two.
  • Parenthesise any rule containing both && and ||. The unbracketed form is not malformed, it answers a different question.
  • Evaluation short-circuits, so put the cheap, most-selective test first — and a guard placed before what it guards genuinely protects it.
  • Chained comparisons are a syntax error, not a silent misread. Use two comparisons and &&.
  • = is not ==. Assigning to a field is always true and rebuilds the record.

You can now write a selection rule that says precisely what you mean, over any combination of text and numeric tests.

Every rule so far has judged each record on its own contents. That is not enough for a report whose logical unit spans several lines — Chapter 3.4 made such a block into one record by changing RS, but sometimes you cannot or should not change what a record is, and you still need "everything from here to there". Chapter 4.3 — Range Patterns is a pattern with memory: it turns on at one match and off at another, and it is the first construct in this track that carries state between records.

Where this fits

Part of the awk curriculum.