awk Cheatsheet

Control Flow and User Functions

Use this awk reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Statements

Same shapes as C, with no types and no declarations.

if (cond) stmt; else stmt
while (cond) stmt
do stmt while (cond)
for (init; cond; incr) stmt
for (key in array) stmt
break        # leave the innermost loop
continue     # next iteration
next         # next input record
nextfile     # next input file
exit [expr]  # jump to END, then quit
return [expr]  # only inside a function
awk '{
  for (i = 1; i <= NF; i++)
    if ($i ~ /^-/) { print "flag:", $i; continue }
}' args.txt

awk 'BEGIN{ i = 0; do { print i } while (++i < 3) }'

- Braces are optional around a single statement, required around several. - ; separates statements on one line, a newline does the same. A statement may break across lines after , { && || do or else. - break or continue outside a loop is an error, not a silent next. - There is no switch in POSIX awk. gawk has one, macOS awk and mawk do not, so chain else if.

Defining a function

function name(param, param) {
  ...
  return value
}
awk '
function max(a, b) { return a > b ? a : b }
function pad(s, n) { while (length(s) < n) s = s " "; return s }
{ print pad($1, 12), max($2, $3) }
' data.txt

- At a call site the name and the ( must touch. max (1, 2) is parsed as concatenation and is a syntax error. In the definition a space is tolerated by both macOS awk and gawk, but POSIX forbids it, so do not write it. - A function may be defined anywhere in the program, before or after its callers, and -f files are concatenated so definitions can live in a library file. - A function with no return returns the empty string, which is also 0. - Recursion works.

awk 'function fact(n) { return n <= 1 ? 1 : n * fact(n - 1) }
     BEGIN { print fact(10) }'      # 3628800

Scope, and the extra-parameter idiom

awk has no local variables. Every variable is global except a function's declared parameters. The convention is to declare extras you never pass, padded with whitespace so readers can see they are locals.

function join(arr, n, sep,    i, out) {     # i and out are locals
  out = arr[1]
  for (i = 2; i <= n; i++) out = out sep arr[i]
  return out
}
awk '
function trim(s) { sub(/^[ \t]+/, "", s); sub(/[ \t]+$/, "", s); return s }
function count_words(line,    n, parts) {   # n and parts are locals
  n = split(line, parts, /[ \t]+/)
  return n
}
{ print count_words(trim($0)) }
' notes.txt

- Scalars are passed by value, arrays are passed by reference. That is the only way to return a collection. - Calling with fewer arguments than parameters is legal, the missing ones start uninitialized. That is what makes the locals idiom work. - Forget the padding and you will overwrite a global i from your main loop, a bug that only shows up on large inputs.

# array out-parameter, since arrays are by reference
awk '
function tally(line, out,    i, n, w) {
  n = split(line, w, /[ \t]+/)
  for (i = 1; i <= n; i++) out[w[i]]++
}
{ tally($0, counts) }
END { for (k in counts) print counts[k], k }
' notes.txt

Reusable function files

cat > lib.awk <<'EOF'
function commify(n,   s, out) {
  s = sprintf("%d", n)
  while (length(s) > 3) {
    out = "," substr(s, length(s) - 2) out
    s = substr(s, 1, length(s) - 3)
  }
  return s out
}
EOF
awk -f lib.awk -f main.awk data.txt

-f is repeatable and the files are concatenated in order, so a library plus a program is the normal way to share helpers. -f - reads the program from stdin.

Record-level flow inside functions

next and nextfile are not allowed inside a function body in POSIX awk, and gawk warns about them. exit is fine and still runs END.

# do this instead of next-in-a-function: return a flag
awk '
function skip(line) { return line ~ /^#/ || line == "" }
skip($0) { next }
{ print NR": "$0 }
' config.ini
StatementIn a ruleIn a functionIn BEGIN/END
next✓✗✗
nextfile✓✗✗
exit✓✓✓
return✗✓✗
getline✓✓✓