Use awk to sum and average a column, filtering rows by a field
From an nginx access.log you need the total and the average bytes sent for successful requests only. Fields are space-separated; the status code is field $9 and the response size in bytes is field $10.
Constraints: single awk invocation, one pass; consider only rows where the status is 200; do not divide by zero when there are no such rows.
# IP - - [time] "GET /path HTTP/1.1" 200 5120
awk '???' access.log
Complete the awk program so it prints the total and the average.
Filter with a pattern, accumulate in the body, print in END: awk '$9==200 { sum+=$10; n++ } END { if (n) print sum, sum/n }' access.log. The $9==200 pattern runs the block only on matching rows, sum+=$10 totals the bytes field, n++ counts them, and END fires once input is exhausted. Guard n so an empty match never divides by zero.
- ✗Writing
$9=200(assignment, always true) instead of$9==200(comparison) - ✗Dividing by
NR(all input rows) rather than the filtered countn - ✗Printing inside the body every row instead of once in the
ENDblock
- →Why does
$9=200match every row while$9==200filters correctly? - →How would you group the totals per status code instead of just
200?
Solution
awk is built as "pattern { action }": the action runs on rows where the pattern is true. Summing goes in the body, and the total prints in END, a block that fires once after all input is read.
awk '$9==200 { sum += $10; n++ } END { if (n) print sum, sum/n }' access.log
rows; the rest are skipped.
average; if (n) guards against a divide-by-zero on an empty match.
$9==200— a pattern condition (==, comparison). The body runs on status-200sum += $10accumulates bytes,n++counts the matching rows.END { if (n) print sum, sum/n }— at end of input prints the total and the
The key traps: $9=200 is an assignment (always true, so every row "matches"), and you must divide by n, not NR (the count of all input rows). One pass, one awk invocation — no grep/bc pipeline needed.