Linux Basic — awk Regex Patterns

2 minute read

awk Regex Patterns

awk is regex-driven by design. Every rule is pattern { action } where the pattern is usually a regex. It uses ERE natively — no -E needed.

awk '/^ERROR/ { print $0 }' app.log

That one-liner is grep '^ERROR' — but awk keeps going where grep stops: column-aware fields, math, conditionals, and accumulators in a single tool.


Pattern Forms

Pattern Runs the action when…
/regex/ the whole line matches regex
$N ~ /regex/ field N matches regex
$N !~ /regex/ field N does not match
condition && /regex/ both true
NR == 1 first line
BEGIN { ... } before any input
END { ... } after all input
# 5th field is a number
awk '$5 ~ /^[0-9]+$/ { print }'

# warnings AND fatal lines
awk '/WARN/ || /FATAL/ { print NR, $0 }'

# skip comments and blanks
awk '!/^[[:space:]]*(#|$)/' config

Field-Level Matching

awk splits each line into $1, $2, … on whitespace by default (or FS):

# users whose shell is /bin/bash (field 7 of /etc/passwd)
awk -F: '$7 ~ /bash$/ {print $1}' /etc/passwd

# IPs (field 1) hitting /api/
awk '$7 ~ /^\/api\// {print $1}' access.log | sort | uniq -c

-F: sets the field separator to :. Use -F'\t' for tabs.


match() and RSTART / RLENGTH

match(string, regex) returns the position of the first match (0 if none) and sets RSTART and RLENGTH:

awk '{
    if (match($0, /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/)) {
        ip = substr($0, RSTART, RLENGTH)
        print ip
    }
}' access.log

Gawk also offers match(str, re, arr) which fills arr with capture groups (gawk extension).


gsub() / sub() — Substitute Inside awk

# strip everything after #
awk '{ sub(/#.*$/, ""); print }'

# normalise multiple spaces
awk '{ gsub(/[[:space:]]+/, " "); print }'

# replace inside a single field, then print all
awk '{ gsub(/foo/, "bar", $3); print }'

sub replaces the first match; gsub replaces all. Both modify in place and return the count of replacements.


Practical One-Liners

# count lines matching a pattern (per file)
awk '/ERROR/ {n++} END{print FILENAME, n+0}' *.log

# sum the 5th field where 2nd field is "in"
awk '$2 == "in" {s += $5} END {print s}' transactions

# print line N (cheap sed -n)
awk 'NR==42' file

# print every 10th line
awk 'NR % 10 == 0' file

# remove duplicate adjacent lines (no sort needed)
awk 'prev != $0; { prev = $0 }' file

# CSV: print row where column 3 looks like an email
awk -F, '$3 ~ /@/ {print $1, $3}' contacts.csv

BEGIN / END for Accumulators

awk '
    BEGIN { FS=":"; print "User,UID" }
    $3 >= 1000 { print $1 "," $3 }
    END   { print "---" NR " entries scanned" }
' /etc/passwd

BEGIN runs once before input; END runs once after. Both can do real work — no if NR==1 hacks needed.


awk vs grep vs sed

Need Best tool
“Does any line match?” — yes/no grep -q
Filter lines by regex, no transform grep
Substitute pattern → replacement sed
Filter on a specific field, do math, accumulate awk

When the question involves columns, arithmetic, or “sum / count / group by”, reach for awk first.


Pitfalls

Pitfall Fix
Slash in regex needs escaping: /\/api\// Or use string match: $1 ~ "/api/"
Forgetting -F for non-whitespace separators awk -F: … for /etc/passwd
print adds newline; printf doesn’t Use printf "%s\n" for control
Reading huge files into arrays OK if you have RAM; otherwise stream

Next → Practice Exercises

Updated: