The Report Skeleton: BEGIN, Body, END
A well-structured AWK report has three parts. The BEGIN block runs once before any input and is where you print headers, set OFS, and initialize totals. The main body runs per record to accumulate data into variables and associative arrays. The END block runs once after all input and is where you print the aggregated results and footer totals. This three-phase shape mirrors a printed report's header, table, and summary line, and separating them keeps the logic clean: gather in the body, present in END.
Cricket analogy: The structure is like a match broadcast: BEGIN is the pre-match presentation setting the scene, the body is ball-by-ball commentary accumulating the score, and END is the post-match summary with the final scorecard.
Formatting Columns with printf
The print statement is fine for quick output but crude for reports because it cannot control width or alignment. printf gives you C-style format specifiers: %-20s left-justifies a string in 20 columns, %10.2f right-justifies a float in 10 columns with two decimals, and %5d pads an integer. A typical report line is printf "%-20s %10.2f %8d\n", name, amount, qty. Unlike print, printf does not add a newline automatically, so you must include \n yourself. Consistent format strings for the header and body rows are what make columns line up into a readable table.
Cricket analogy: printf is like a properly ruled scorecard where each column has a fixed width for runs, balls, and strike rate, unlike scribbling figures that never line up under the headings.
# sales.txt columns: region product amount
# Report: total amount per region, aligned, with grand total
awk '
BEGIN {
printf "%-12s %12s\n", "REGION", "TOTAL"
printf "%-12s %12s\n", "------", "-----"
}
{
total[$1] += $3
grand += $3
}
END {
for (r in total)
printf "%-12s %12.2f\n", r, total[r]
printf "%-12s %12s\n", "------", "-----"
printf "%-12s %12.2f\n", "GRAND", grand
}
' sales.txtGrouped Subtotals and Sorted Output
Reports usually need grouping and often a stable sort order. Associative arrays give the grouping: total[$1] += $3 accumulates a subtotal per key. AWK's array iteration order is unspecified, so to present rows sorted you either pipe the END output through sort, or in GNU AWK call asorti(total, keys) to sort the keys into an indexed array and iterate that. For running subtotals within a sorted input, track the current group in a variable and, when the key changes, flush the subtotal before resetting, a classic control-break report pattern.
Cricket analogy: Grouped subtotals are like partnership totals in an innings: you keep a running stand for the current pair and, when a wicket falls, you record that partnership before the next pair begins.
In GNU AWK (gawk), asort() sorts array values and asorti() sorts array indices into a new array, giving you deterministic report ordering without an external sort. In POSIX AWK there is no built-in sort, so piping the END output to the sort command is the portable choice.
Never assume for (k in arr) iterates in insertion or sorted order — AWK's traversal order is implementation-defined and effectively random. If your report needs a stable order, sort explicitly with asorti() or an external sort; relying on hash order produces reports that reshuffle unpredictably.
- Structure reports as BEGIN (headers/init), body (accumulate), END (present totals).
- Use printf, not print, for width and alignment; remember to add \n yourself.
- Format specifiers: %-Ns left-justify strings, %N.2f right-justify floats, %Nd pad integers.
- Associative arrays produce grouped subtotals keyed by any field.
- Array iteration order is unspecified; sort keys with asorti() (gawk) or pipe to sort.
- Control-break reporting flushes a subtotal when the sorted group key changes.
- Maintain a grand-total variable across the body and print it in END as the footer.
Practice what you learned
1. Which AWK block runs exactly once after all input has been read?
2. What does the printf specifier `%-20s` do?
3. Why must you add \n in printf statements?
4. How do you get deterministic ordering of report rows from an associative array in gawk?
5. What is a control-break report pattern?
Was this page helpful?
You May Also Like
Log File Analysis with AWK
Practical techniques for parsing, filtering, and aggregating server and application logs with AWK, including field extraction, time-range filtering, and building counters.
AWK in Shell Pipelines
How to slot AWK into Unix pipelines as a field-aware filter and transformer, passing data between grep, sort, cut, and other tools via stdin and stdout.
Performance Tips for AWK
Practical techniques to make AWK programs faster: cutting work per record, choosing the right implementation, minimizing regex cost, and avoiding wasteful shell patterns.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics