Skip to content
VLSI Mentor

awk Part I · Module 3 · Fields, Records, and Separators

Positional Fields: $1, $2, $3

Module 2 explained how awk splits a record into fields. This module is about controlling that process, and it starts with the simplest form of control: choosing columns by their position. On a two-column file that is barely a decision. On a timing report with five or six columns it becomes one, because you have to pick which numbers to trust and in what order to emit them. This chapter works on a report wide enough for that to matter — selecting a subset, reordering on the way out, filtering on one column while printing another — and then makes the uncomfortable point that a column number is a promise about the report's layout. When a tool upgrade inserts a column, every script that hard-coded a number keeps running and starts being wrong.

Foundation15 min readawkPart IFieldsSeparatorsEDA Reports

Part I · Module 3 · Chapter 3.1 · Positional Fields: $1, $2, $3

1. Why This Matters

Chapter 2.2 answered a mechanical question: given a record, how does awk decide where the fields are? The answer was the default whitespace rule, and it is doing all the work in this chapter too.

What it did not answer is the engineering question. Given a report with six columns, which ones do you read, and what happens to your script when the report changes?

That is this module's subject, and position is where it starts:

$4 is not the name of a value. It is the fourth field, and it means what you think only while the report keeps that value fourth.

Everything up to section 6 is the straightforward practice. Sections 6 and 7 are why the chapter is worth reading.

2. A Wider Report

Two columns were enough for Module 2. They are not enough to make position interesting, so here is a synthetic timing report with five:

Azvya Education Pvt. Ltd.VLSI Mentor
timing.rpt — synthetic teaching data. Real timing reports differ by tool and version; this one is a reduced, one-row-per-path table.
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

Five fields per record: an endpoint path, the check type, the clock, the slack, and a verdict.

Notice the columns are padded with different numbers of spaces so they line up on screen. That does not affect anything — Chapter 2.2 established that a run of whitespace is one separator — which is exactly why the default rule is the right tool for a report that was formatted for a human.

FieldHoldsOn record 1
$1endpoint pathu_core/u_alu
$2check typesetup
$3clockclk_main
$4slack-0.124
$5verdictVIOLATED

That table is the contract this chapter is about. Keep it in mind for section 7.

3. What Counts as One Field

Before relying on those positions, it is worth checking an assumption that trips people up on EDA reports specifically: an instance path looks like several things joined together, and awk treats it as one field.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR == 1 { print "NF=" NF; print "  $1=[" $1 "]" }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
NF=5
  $1=[u_core/u_alu]

Five fields, and the whole path is $1. Slashes are not whitespace, so they do not split anything. The same holds for the bracketed bit-selects that appear in real netlist paths:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf 'u_core/u_alu/reg_q[3] setup -0.124\n' > brackets.rpt
awk '{ print NF, "[" $1 "]" }' brackets.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3 [u_core/u_alu/reg_q[3]]

Three fields. The path survives intact despite slashes, an underscore and square brackets, because none of them is whitespace and Chapter 2.2's default rule only splits on whitespace.

4. Selecting Columns by Position

The path and its slack, which is the pair most timing questions want:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, $4 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu -0.124
u_core/u_regfile 0.081
u_pad/u_io -0.052

Two of the five columns, in the order written, separated by a space. The other three were read and discarded — awk split the whole record either way, and you simply did not ask for them.

Three columns, adding the clock:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, $3, $4 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu clk_main -0.124
u_core/u_regfile clk_main 0.081
u_pad/u_io clk_io -0.052

The comma between each pair is what inserts the space. Chapter 1.6 covered that; the variable controlling which character it inserts is OFS, and Chapter 3.3 is where you get to change it.

5. Reordering on the Way Out

The order you print in has nothing to do with the order in the file:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $4, $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-0.124 u_core/u_alu
0.081 u_core/u_regfile
-0.052 u_pad/u_io

Slack first, path second. This is genuinely useful — putting the number first makes the output sortable by slack with a following sort -n, and it is the shape a spreadsheet usually wants.

Note what did not happen: the record itself was not modified. $0 still holds the original line in its original order. Rewriting the record rather than just printing a chosen order is a different operation with different consequences, and it is Chapter 3.6's subject.

6. Position Is a Contract

Everything above works, and it all rests on an assumption that is nowhere written down: that slack is in column 4.

That assumption is a contract with the report's layout. You did not negotiate it, the tool did not promise it, and nothing in your script states it. It is simply true of the file you happened to look at.

7. When a Column Is Inserted

Here is the contract being broken. The tool is upgraded and the new version reports the delay-check type as well, inserting one column before slack:

Azvya Education Pvt. Ltd.VLSI Mentor
timing_v2.rpt — synthetic. The same two paths, with a 'max' column inserted before slack.
u_core/u_alu    setup  clk_main  max  -0.124  VIOLATED
u_core/u_regfile setup clk_main  max   0.081  MET

Now run the command from section 4 — the one that was correct yesterday — completely unchanged:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, $4 }' timing_v2.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu max
u_core/u_regfile max

The slack column is gone from the output. In its place is the word max, for every path.

What makes it worse is that the fix is invisible from inside the script. There is nothing wrong with $4. The report moved.

8. Which End Is Stable?

There is a partial defence, and it is worth seeing now because it sets up the next chapter.

In this particular change, a column was inserted in the middle. The verdict stayed last, and the slack stayed second-from-last. So counting from the end survives the change:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, $(NF-1) }' timing_v2.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu -0.124
u_core/u_regfile 0.081

Correct slack from the new format, with no change to how it was addressed. And the same expression also works on the original:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, $(NF-1) }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu -0.124
u_core/u_regfile 0.081
u_pad/u_io -0.052

One expression, both layouts.

9. Filtering on One Column, Printing Another

Position applies to patterns as well as to actions, and the two need not be the same column:

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

The pattern tests column 4 numerically; the action prints columns 1 and 4. The + 0 forces arithmetic, which Chapter 1.2 showed matters the moment a value carries a unit suffix.

A text comparison on a different column again:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$5 == "VIOLATED" { print $1 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu
u_pad/u_io

Both commands select the same two records here, by two independent routes — one arithmetic on the slack, one exact text on the verdict. That redundancy is useful: if they ever disagree on a real report, the report is telling you something about itself.

10. Two Value Columns

Reports often carry more than one number per row, and position is how you keep them apart. A synthetic cell report with a count and an area:

Azvya Education Pvt. Ltd.VLSI Mentor
cells.rpt — synthetic. A cell type, an instance count, and a total area.
DFF 120 1820
AND 45 180
MUX 18 144
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $1, $2, $3 }' cells.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
DFF 120 1820
AND 45 180
MUX 18 144

Two independent totals from one pass:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ count += $2; area += $3 } END { print count, area }' cells.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
183 2144

183 cells and 2144 area units. Keeping them in separate accumulators is only possible because the two columns are separately addressable, and getting the positions the wrong way round would produce two plausible numbers with their meanings swapped.

Chapter 1.6's caveat stands and is the reason this example exists: 183 is not a size and 2144 is not a cell count. They are sums of two different columns in two different units, and the only thing keeping them straight is $2 and $3.

11. A Header Row Changes the Arithmetic

Many reports begin with a line naming the columns, and it is worth knowing exactly what that does to positional reading — because the answer is not what you would guess.

Azvya Education Pvt. Ltd.VLSI Mentor
hdr.rpt — synthetic. The same report with a header row.
endpoint check clock slack verdict
u_core/u_alu setup clk_main -0.124 VIOLATED
u_pad/u_io hold clk_io -0.052 VIOLATED

Sum the slack column without thinking about the header:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ s += $4 } END { print s }' hdr.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-0.176

That total is correct. -0.124 + -0.052 is -0.176, and the header contributed nothing — because $4 on the header row is the word slack, which counts as 0 in arithmetic. The header was included and harmlessly added zero.

Now count the records:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'END { print NR }' hdr.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3

Three, for two paths. This number is wrong, and nothing about the sum being right warned you.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR > 1 { s += $4; n++ } END { print n, s }' hdr.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 -0.176

NR > 1 skips the header, and now both numbers are right. Chapter 2.1 introduced NR; this is the commonest reason to use it.

12. Common Mistakes

Treating a column number as a name for the value. Section 6. $4 means "fourth field"; it means "slack" only by convention.

Assuming a command that worked last quarter still reads the right column. Section 7. A tool upgrade is a layout change, and nothing warns you.

Reaching for $(NF-1) as a general fix. Section 8. It trades one assumption for a different one.

Forgetting the comma in print. print $1 $4 concatenates with nothing between them, giving u_core/u_alu-0.124. The comma is what inserts a separator.

Counting from zero. $1 is the first field; $0 is the whole record. Chapter 1.6 flagged it and it is still the most common slip when switching from another language.

Comparing a numeric column as text. $4 < 0 works on clean numbers but becomes a string comparison the moment the field is not a clean number — Chapter 1.2's unit-suffix trap. $4 + 0 < 0 forces arithmetic.

Reading a column that does not exist. $7 on a five-field record is the empty string, which is 0 in arithmetic. Chapter 2.2 covered the rule; the consequence here is a total of 0 from a column you misnumbered.

13. Exercises

Exercise 1 — Predict the output

Azvya Education Pvt. Ltd.VLSI Mentor
timing.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
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $2 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup
setup
hold

The check type, one per record. If you expected the clock, recount — the path is a single field despite containing slashes, because slashes are not whitespace.

Exercise 2 — Modify a command

Print the clock and the verdict, in that order, for every record.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $3, $5 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk_main VIOLATED
clk_main MET
clk_io VIOLATED

Two positions, chosen independently of their order in the file. Note $5 and $NF would both work here; which one you should prefer is Chapter 3.2's question.

Exercise 3 — Trace a shift

Without running it, work out what this prints, then check. The file is the six-column version.

Azvya Education Pvt. Ltd.VLSI Mentor
timing_v2.rpt
u_core/u_alu    setup  clk_main  max  -0.124  VIOLATED
u_core/u_regfile setup clk_main  max   0.081  MET
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ print $3, $4, $5 }' timing_v2.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk_main max -0.124
clk_main max 0.081

Every position after the insertion has moved one to the right. $4 is now the delay type, $5 is the slack, and the slack that used to be $4 is nowhere in the places you expected. Tracing a shift by hand once is the fastest way to stop trusting hard-coded numbers unconditionally.

Exercise 4 — Debug a wrong total

An engineer sums the slack column of timing_v2.rpt with the command below and gets 0. Explain why, then produce the correct total.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ s += $4 } END { print s }' timing_v2.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0

$4 on the six-column report is the word max, and max in arithmetic is 0. Adding 0 twice gives 0 — a perfectly valid sum of the wrong column, with no error and exit status 0.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ s += $5 } END { print s }' timing_v2.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-0.043

-0.124 + 0.081 is -0.043. The lesson is not "use $5" — it is that the first command could not tell you anything was wrong, so the check has to come from outside it. Exercise 6 builds that check.

Exercise 5 — Engineering application

Using the five-column report, print the path and slack of every record whose verdict is VIOLATED, and report how many there were.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '$5 == "VIOLATED" { print $1, $4; n++ } END { print n + 0, "violations" }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_core/u_alu -0.124
u_pad/u_io -0.052
2 violations

The pattern reads the verdict column and the action reads the path and slack columns — three positions, one pass. The + 0 keeps a clean report printing 0 violations rather than a blank, which Chapter 1.6 established and Chapter 2.4 explained.

Two caveats carried forward. VIOLATED is the tool's own verdict, not an independent check — Chapter 1.2's distinction between a recorded result and a measurement. And 2 counts records, so a report listing the same physical path under two checks counts it twice.

Exercise 6 — Challenge: state the contract in the script

Write a command that reads slack from $4 but refuses to produce a number if the report does not have the layout that assumption requires. Test it against both report versions.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NF != 5 { print "ERROR: expected 5 fields, found", NF, "on record", NR; bad++; next } { s += $4 } END { if (bad) exit 2; print "total slack:", s }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
total slack: -0.095
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NF != 5 { print "ERROR: expected 5 fields, found", NF, "on record", NR; bad++; next } { s += $4 } END { if (bad) exit 2; print "total slack:", s }' timing_v2.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ERROR: expected 5 fields, found 6 on record 1
ERROR: expected 5 fields, found 6 on record 2

The second command printed no total at all and exited 2, which a calling script can test. The implicit contract from section 6 is now written down as NF != 5, and the shift from section 7 is caught on the first record instead of producing max.

This is the pattern the rest of the module keeps returning to: an assumption you can state is an assumption you can check. NF is what makes it checkable, and that is what the next chapter is about.

14. Summary

  • A column number is a contract with the report's layout, not a name for the value. $4 means "the fourth field".
  • Selecting and reordering are independent of the file. print $4, $1 emits slack first without touching the record; rewriting the record is Chapter 3.6.
  • The comma in print inserts the separator. Without it, values are concatenated with nothing between them.
  • One inserted column breaks every hard-coded number after it, with no error and exit status 0. The word max arriving where slack belonged counts as 0 in arithmetic, so a downstream negative-slack check finds nothing.
  • Counting from the end is not a general fix. It assumes the trailing columns are stable, just as counting from the start assumes the leading ones are. A report change breaks whichever assumption it contradicts.
  • Patterns and actions can read different columns, which is how you filter on a verdict and print a slack.
  • Separate value columns need separate accumulators, and only position keeps their meanings apart.
  • An assumption you can state is an assumption you can check. NF != 5 turns an implicit layout requirement into a loud failure.

You can now pull any column out of a wide report and, more importantly, say what your command is assuming about that report.

Section 8 left a question open and Exercise 6 answered half of it. If counting from the start assumes the head is stable and counting from the end assumes the tail is, then the number that tells you which one you actually have is the field count itself. Chapter 3.2 — NF and the Last Field is about using NF as the shape of a record rather than just a number, and about choosing deliberately between a fixed position and $NF.

Where this fits

Part of the awk curriculum.