awk & sed
Two small, decades-old text-processing tools that still outrun a script for anything line- or column-shaped.
grep to find the lines, awk to pull out
and compute over a column, sed to reshape the text of what's left.
pattern { action } pairs. For every input line, awk checks
each pattern in order and runs the matching action; a bare pattern with no action defaults to
{ print }, and a bare action with no pattern runs on every line.
echo "alice 30 engineer" | awk '{ print $1, $3 }'
# alice engineer
Before running any action, awk splits the current line ($0) into fields on whitespace by
default: $1 is the first field, $2 the second, and so on up to
$NF, the last one.
awk '{ sum += $2 } END { print sum }' sales.txt
awk '{ if ($2 > max) max = $2 } END { print max }' sales.txt
awk '{ count[$1]++ } END { for (k in count) print k, count[k] }' log.txt
Variables in awk don't need to be declared; an unseen variable starts as an empty string/zero. This
makes running totals, counts, and maximums trivial one-liners — the pattern above (accumulate in
the main block, report in END) covers most of what people reach for awk to do.
function average(arr, sum, n, k) { # extra params after the real one are local vars, by convention
for (k in arr) { sum += arr[k]; n++ }
return n ? sum / n : 0
}
BEGIN {
scores["alice"] = 90
scores["bob"] = 75
print average(scores)
}
echo "hello world" | sed 's/world/bash/' # hello bash
s/pattern/replacement/ replaces only the first match on each line by default.
sed -E 's/(foo|bar)baz/\1/' file
-E (or the older -r) enables extended regular expressions: unescaped ( ), +, ?, and | for alternation, instead of needing backslash-escaped \( \) \+ \? in sed's default basic regex mode
h copies pattern space into hold space, H appends it instead of overwriting,
g copies hold space into pattern space, G appends it, and x
swaps the two outright. This is what makes sed capable of operating across lines, not just within one.
sed -n '1!G;h;$p' fileThis is the classic sed one-liner for reversing a file's line order (equivalent to
tac):
on every line except the first, it prepends whatever has accumulated in the hold space so far
(1!G), saves the growing, reversed result back to the hold space (h), and
only prints once it reaches the last line ($p), by which point the whole file has been
built up in reverse.