Skip to content

AWK Cheat Sheet: Commands, Variables, Functions & One-Liners

A tested AWK cheat sheet: syntax, built-in variables, patterns, string functions, printf, arrays, one-liners and the gawk vs mawk differences.

Dasun Kalupahana 5 min read 68 views 1 comment
On this page
  1. Sample files used below
  2. Run AWK
  3. Program structure
  4. Fields and built-in variables
  5. Patterns
  6. Operators
  7. String functions
  8. Numbers and printf
  9. Control flow
  10. Arrays
  11. Input and output
  12. User-defined functions
  13. One-liners
  14. What works in gawk but not in mawk
  15. Common mistakes

This is a reference to keep open while you work: the AWK syntax you use most, grouped by task, with the result of each example. Every entry was run on gawk 5.2.1 and mawk 1.3.4. Where the two disagree, the entry says so, and a portability table near the end collects the differences in one place. Print this page from your browser if you want a paper copy.

If you are new to AWK, read the beginner's guide first. This sheet assumes you know what a field and a record are.

Sample files used below

printf 'start\nx\ny\nend\nz\n' > rng.txt
printf 'one two\nthree four five\n\nsix\n' > t4.txt
printf 'p1 l1\np1 l2\n\np2 l1\n' > para.txt

Run AWK

WhatCommand
Program on the command lineawk 'pattern {action}' file
Read from a pipecommand | awk '...'
Field separatorawk -F, '...' file (details in AWK field separators)
Set a variableawk -v name=value '...' file
Program from a fileawk -f script.awk file
Show the versionawk --version or awk -W version

Wrap the program in single quotes. Double quotes let the shell replace $1 before AWK sees it.

Program structure

BEGIN { ... }          # once, before any input
pattern { action }     # for every line that matches the pattern
{ action }             # no pattern: every line
pattern                # no action: print the matching lines
END { ... }            # once, after the last line

Fields and built-in variables

VariableMeaning
$0The whole current line
$1, $2, ...Field 1, field 2, ...
NFNumber of fields on this line ($NF is the last, $(NF-1) the one before)
NRLine number across all input
FNRLine number within the current file
FILENAMEName of the current file
FSInput field separator (default: runs of spaces and tabs)
OFSOutput field separator, used between items in print a, b (default: a space)
RSInput record separator (default: newline). RS="" reads paragraphs separated by blank lines
ORSOutput record separator (default: newline)
ENVIRON["NAME"]An environment variable
ARGC, ARGV[i]The command-line arguments
RSTART, RLENGTHSet by match()
SUBSEPJoins the parts of a multi-dimensional array key
awk '{print FILENAME, NR, FNR}' t4.txt rng.txt | sed -n '3,6p'
# t4.txt 3 3
# t4.txt 4 4
# rng.txt 5 1
# rng.txt 6 2

awk 'BEGIN{RS=""} {print NR ": " $1, NF}' para.txt
# 1: p1 4
# 2: p2 2

awk 'BEGIN{ORS="|"} {print $1} END{printf "\n"}' t4.txt
# one|three||six|

echo 'a b c' | awk '{NF=2; print}'        # a b    (assigning NF cuts the line)
echo 'a b'   | awk '{$5="e"; print NF}'    # 5      (assigning past the end adds empty fields)

Patterns

PatternSelectsResult on the sample files
NR==2Line number 2awk 'NR==2' t4.txt gives three four five
NR%2Odd linesrng.txt: start, y, z
NR%2==0Even linesrng.txt: x, end
NR>=2 && NR<=3A range of line numbersrng.txt: x, y
/start/,/end/From a matching line to the next matching linerng.txt: start, x, y, end
/regex/Lines matching a regular expressionawk '/x/{c++} END{print c+0}' rng.txt gives 1
!/regex/Lines that do not matchawk '!/x/' rng.txt skips x
$1 ~ /^t/Field 1 matchest4.txt: three four five
$1 !~ /^t/Field 1 does not matcht4.txt: one two, the blank line, six
NFNon-empty linest4.txt: 3 lines (the blank one is dropped)
!NFBlank linesawk '!NF' t4.txt | wc -l gives 1
length > 8Lines longer than 8 characterst4.txt: three four five

Operators

KindOperatorsExample and result
Arithmetic+ - * / % ^2^10 is 1024, 7%3 is 1, 7/2 is 3.5, int(7/2) is 3, -7%3 is -1
Assignment= += -= *= /= ++ --x=5; x++; x+=10; x*=2 leaves x at 32
Comparison< <= == != >= >("10" < "9") is 1 (text compare), (10 < 9) is 0 (number compare)
Concatenation(nothing between values)a="foo"; b="bar": a b is foobar, a "-" b is foo-bar
Conditionalcond ? yes : non=5; (n%2==0 ? "even" : "odd") gives odd
Membershipkey in array("k" in a) is 1, ("z" in a) is 0
Logic&& || !Used in patterns and conditions

The ** power operator works in gawk only; mawk gives a syntax error. Use ^, which works in both.

String functions

FunctionDoesExample and result
length(s)Length of a stringlength("Hello, World") is 12
substr(s, start, len)Part of a string, counting from 1substr(s,8) is World, substr(s,1,5) is Hello
index(s, t)Position of t in s, or 0index(s,"World") is 8
toupper(s), tolower(s)Change caseHELLO, WORLD and hello, world
sub(re, repl, s)Replace the first match in s; returns 1 or 0s="foo bar foo" becomes X bar foo
gsub(re, repl, s)Replace every match; returns the counts="foo bar foo" becomes X bar X (returns 2)
match(s, re)Find a match; sets RSTART and RLENGTHOn price: 42 USD with /[0-9]+/: 8 2, and substr gives 42
split(s, arr, sep)Split into an array; returns the countsplit("a,b,c",p,",") returns 3, with p[1]="a", p[3]="c"
sprintf(fmt, ...)Format like printf, return a stringsprintf("%05.1f|%-4s|%3d", 3.14159, "ab", 7) is 003.1|ab | 7
awk 'BEGIN{s="a-b-c"; gsub(/-/,"&&",s); print s}'                 # a--b--c   (& means "the matched text")
awk 'BEGIN{n=split("a1b22c",p,/[0-9]+/); print n, p[1], p[2], p[3]}'  # 3 a b c   (separator can be a regex)
awk 'BEGIN{s="  trim me  "; gsub(/^ +| +$/,"",s); print "[" s "]"}'   # [trim me]

Two things to remember. First, a string used as a pattern is a regular expression, so gsub(".", "-", s) replaces every character, not every dot. On a.b.c it gave -----, while gsub(/\./, "-", t) gave a-b-c. Second, substr() with a start below 1 is not portable: substr("hello", -1, 3) gave hel in gawk and hell in mawk. Keep the start at 1 or more.

Numbers and printf

FunctionResult
int(x)int(3.9) is 3, int(-3.9) is -3
sqrt(x), exp(x), log(x)sqrt(16) is 4, exp(0) is 1, log(1) is 0
sin(x), cos(x), atan2(y,x)sin(0) is 0, cos(0) is 1, atan2(0,-1) is 3.14159
rand(), srand(seed)rand() returns a number from 0 up to (not including) 1. The actual numbers differ between awks, so do not compare them

Plain print shows at most six significant digits, so use printf when exact output matters. A single line that uses most formats:

awk 'BEGIN{printf "%d %5.2f %-6s| %05d %x %o %e %c %%\n", 42.9, 3.14159, "ab", 42, 255, 8, 12345.678, 65}'
# 42  3.14 ab    | 00042 ff 10 1.234568e+04 A %
FormatMeaning
%dInteger (42.9 prints as 42)
%5.2fDecimal number, width 5, 2 digits after the point
%-6sString, width 6, left-aligned
%05dInteger padded with zeros to width 5
%x, %oHexadecimal, octal
%eScientific notation
%cCharacter from a number (65 gives A)
%%A literal percent sign

printf does not add a newline. End the format with \n.

Control flow

awk 'BEGIN{for(i=1;i<=3;i++) printf "%d ", i; print ""}'                       # 1 2 3
awk 'BEGIN{i=1; while(i<=3){printf "%d ", i; i++} print ""}'                  # 1 2 3
awk 'BEGIN{i=5; do {printf "%d ", i; i++} while(i<3); print ""}'              # 5   (do-while runs once first)
awk 'BEGIN{for(i=1;i<=10;i++){if(i%2) continue; if(i>6) break; printf "%d ", i} print ""}'   # 2 4 6
awk 'BEGIN{x=7; if (x<5) print "low"; else if (x<10) print "mid"; else print "high"}'          # mid
StatementEffect
nextStop with this line and go to the next one. NR==2{next} {print} skips line 2
nextfileSkip the rest of the current file. FNR==2{nextfile} {print FILENAME": "$0} printed one line from each of two files
exit nStop and set the exit status. NR==2{exit 3} exited with 3. An END block still runs after exit: BEGIN{exit 5} END{print "end still runs"} printed that text and exited with 5

Arrays

TaskCode
Set and reada["key"]=1; print a["key"]
Test for a keyif ("key" in a) ... (does not create the key)
Count the entrieslength(a) (works in gawk and mawk)
Remove one key / all keysdelete a["key"] / delete a
Loop over keysfor (k in a) print k, a[k] (order is not guaranteed, pipe to sort)
Split into an arrayn=split("c a b", w) gives w[1]="c", w[2]="a", w[3]="b"
Two-part keya[1,2]="v"; split the key back with split(k, p, SUBSEP)
Count by value{c[$1]++} END {for (k in c) print k, c[k]}
Remove duplicate lines!seen[$0]++

Input and output

TaskCode
Write to a fileprint $1 > "out.txt"
Pipe to a commandprint $1 | "sort -r", then close("sort -r") in END so the command finishes before AWK exits
Write to standard errorprint "message" > "/dev/stderr"
Run a shell commandsystem("echo from shell")
Read a command's output"echo hello" | getline x sets x to hello
Read a file line by linewhile ((getline line < "rng.txt") > 0) n++ counted 5 lines

Close a file with close("out.txt") before you read it back in the same program. I wrote $1 of every line of t4.txt to a file, closed it, and read it back with getline, and got one, three, an empty line and six.

User-defined functions

function max(a, b) { return a > b ? a : b }
function fact(n)   { return n <= 1 ? 1 : n * fact(n - 1) }
function f(x,  tmp) { tmp = x * 2; return tmp }   # extra parameters are local variables

BEGIN { print max(3, 9), max("b", "a") }          # 9 b
BEGIN { print fact(5) }                           # 120
BEGIN { tmp = "keep"; print f(4), tmp }           # 8 keep

AWK has no local keyword. Extra parameters you never pass behave as local variables, which is why tmp kept its value in the last line. By habit, put two spaces before them to show they are locals.

One-liners

GoalCommand
Number the linesawk '{print NR": "$0}' rng.txt (first line: 1: start)
Print line 3awk 'NR==3' rng.txt (y)
Print lines 2 to 3awk 'NR>=2 && NR<=3' rng.txt
Print everything after line nawk -v n=2 'NR>n' rng.txt (y, end, z)
Print the last lineawk 'END{print}' rng.txt (z)
Print every other lineawk 'NR%2' rng.txt (start, y, z)
Delete blank linesawk 'NF' t4.txt
Remove duplicate linesawk '!seen[$0]++' file
Reverse the linesawk '{a[NR]=$0} END{for(i=NR;i>0;i--) print a[i]}' rng.txt (starts z, end, y)
Join lines with commasawk '{printf "%s%s", sep, $0; sep=","} END{print ""}' rng.txt (start,x,y,end,z)
Trim leading and trailing spaceawk '{gsub(/^[ \t]+|[ \t]+$/, ""); print}'
Find the longest lineawk '{if (length > max) {max=length; line=$0}} END{print max, line}' t4.txt (15 three four five)
Count lines and wordsawk '{w+=NF} END{print NR, w}' t4.txt (4 6)
Swap the first two fieldsecho 'a b c' | awk '{t=$1; $1=$2; $2=t; print}' (b a c)
Capitalise the first wordecho 'hello world' | awk '{print toupper(substr($1,1,1)) substr($1,2)}' (Hello)
Print all but the first fieldecho 'a b c' | awk '{$1=""; print substr($0,2)}' (b c)
Count matching linesawk '/x/{c++} END{print c+0}' rng.txt (1)
Sum a columnawk '{s+=$1} END{print s}' file (see summing a column)
Print a columnawk '{print $2}' file (see printing a column)

What works in gawk but not in mawk

Most of this sheet runs on every awk. These features do not. The worst ones fail with no message at all, so your script runs and gives wrong output.

Featuregawkmawk 1.3.4
gensub()WorksError: function never defined
asort(), asorti()WorkError: function never defined
** power operatorWorksSyntax error
IGNORECASE=1Case-insensitive matchingSilently ignored
PROCINFO["sorted_in"]Sorts for (k in a)Silently ignored (loop order differs)
BEGINFILE, ENDFILERun per fileSilently do nothing
FIELDWIDTHS, FPATWorkSilently ignored
UTF-8 charactersWith a UTF-8 locale, length("héllo") is 56 (counts bytes), even in a UTF-8 locale
Division by zeroFatal errorPrints -nan and carries on
0x1A + 0026
-i inplace, --csv (5.3+)gawk options (-i inplace was run on gawk 5.2.1; --csv needs 5.3+ and was not run)Not tested

These do work in both: length(array), nextfile, delete array, systime(), strftime(), /dev/stderr, -v and -f.

Common mistakes

  • Double quotes around the program. awk "{print $1}" prints whole lines, because the shell empties $1.
  • Header row in a numeric test. $4 > 5 also matches a header like qty. Add NR>1 &&.
  • Array loop order. for (k in a) has no guaranteed order. Pipe to sort.
  • Setting FS inside a rule. It only affects the next line. Set it in BEGIN or use -F.
  • $(NF-1) on a blank line. It stops AWK with an error. Guard with NF>1.
  • A string where you meant a regex. gsub(".", ...) matches any character.

Free resources for this topic

Code snippets and fixes for this topic

Get one practical tip every week

New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.

Have a question about this post?

Ask the community: other developers and the author can answer.

Ask the community

Get notified about new articles

One click, no email needed. Turn it off anytime with the bell at the top.

Get new articles on your iPhone

Add DEV Talk to your Home Screen, open it, then tap the bell.

Rate this post
0.0
Share

Enjoying the content?

If this article helped you, you can support the author with a coffee. It keeps new posts coming.

Buy me a coffee (opens in a new tab)

Comments 1

Be kind. Comments with links are reviewed before they appear.

Anuradha

Good work, well written and informative!

Get new articles in your inbox

Practical tutorials and deep dives, about once a week. No spam, unsubscribe anytime.

Privacy policy

Add DEV Talk to your Home Screen

  1. 1 Tap the Share button in Safari's toolbar.
  2. 2 Scroll down and tap Add to Home Screen.
  3. 3 Tap Add. Open it from the Home Screen to read like an app and to turn on notifications with the bell.

We use cookies to improve your experience and analyze traffic. By continuing, you agree to our Privacy Policy.