Build the mental model
Where grep finds lines and sed transforms text, awk thinks in a fundamentally different unit: records and fields. By default it splits each line on whitespace (or, with -F, whatever delimiter you specify) and lets you refer to the pieces positionally as $1, $2, and so on, with $0 always meaning the entire line, NF meaning the number of fields on the current line, and NR meaning the current line's record number — a running counter across the whole input. This field-based model makes awk the natural tool the moment your data is organized into columns: a CSV export, a colon-separated file like /etc/passwd, or the columnar output of commands like ps or df. What sets awk apart from grep and sed even more is that it isn't limited to processing one line in isolation — it can carry state across every line it reads using ordinary variables, and then act on that accumulated state once, after the last line, inside an END block. This is exactly how you compute a running total or an average in a single pass over the data instead of reading the file twice or writing a small program in another language: a variable like sum accumulates inside the main pattern-action block on every line, and the END block, which runs exactly once, is where you print the final aggregated result.
Connect it to a real scenario
Suppose you have a CSV export of employee names, departments, and salaries and need to list everyone in one department or compute the total payroll — this lesson's data_file, with rows like alice,engineering,72000, is exactly this scenario. grep could find lines containing the word 'engineering', but it has no concept of columns, so it can't isolate just the name field or exclude a department name that happens to appear inside someone's job title; sed isn't built for arithmetic at all. awk closes this gap directly. Setting -F, tells awk to split each line on commas instead of whitespace, which is what makes $1, $2, and $3 correspond to name, department, and salary respectively — as shown by awk -F, '{print $1}' for just the names and awk -F, '{print $1, $2}' for name and department together. Filtering by column is a pattern-action pair: $2 == "engineering" { print $1 } only runs the print action on lines where the second field matches, which is how the code lists just the engineering employees without touching sed or grep at all. The two aggregation lines show awk's accumulation model directly: {sum += $3} END {print sum} adds the salary field into sum on every line and prints it once at the end for the payroll total, while adding a count++ alongside it and dividing sum by count in the END block computes the average in the same single pass.
Try the working example
#!/bin/bash
data_file=$(mktemp)
cat > "$data_file" <<EOF
alice,engineering,72000
bob,sales,54000
carol,engineering,81000
dave,marketing,60000
EOF
echo "First field (names) for each line:"
awk -F, '{print $1}' "$data_file"
echo ""
echo "Name and department (fields 1 and 2):"
awk -F, '{print $1, $2}' "$data_file"
echo ""
echo "Number of fields on the first line:"
awk -F, 'NR==1 {print NF}' "$data_file"
echo ""
echo "Only engineering employees:"
awk -F, '$2 == "engineering" {print $1}' "$data_file"
echo ""
echo "Total salary across all employees:"
awk -F, '{sum += $3} END {print sum}' "$data_file"
echo ""
echo "Average salary:"
awk -F, '{sum += $3; count++} END {print sum / count}' "$data_file"
rm -f "$data_file"Running this script prints:
First field (names) for each line:
alice
bob
carol
dave
Name and department (fields 1 and 2):
alice engineering
bob sales
carol engineering
dave marketing
Number of fields on the first line:
3
Only engineering employees:
alice
carol
Total salary across all employees:
267000
Average salary:
667505-minute try-it
Create a colon-separated text file with three columns — product name, category, and price — for four products, then write an awk command that prints only the name and price for products in one specific category, and a second command that prints the total price across all products.
One important caution
Forgetting to set -F for a non-whitespace delimiter, so a CSV or colon-separated line is treated as a single field instead of being split correctly
Reaching for grep or sed to pull out and total a specific column, which requires fragile regex tricks, when awk does it directly and far more reliably
GNU Awk User's Guide — Bash / Shell Scripting