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
| What | Command |
|---|---|
| Program on the command line | awk 'pattern {action}' file |
| Read from a pipe | `command \ |
| Field separator | {% raw %}awk -F, '...' file (details in AWK field separators) |
| Set a variable | awk -v name=value '...' file |
| Program from a file | awk -f script.awk file |
| Show the version |
awk --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
| Variable | Meaning |
|---|---|
$0 |
The whole current line |
$1, $2, ... |
Field 1, field 2, ... |
NF |
Number of fields on this line ($NF is the last, $(NF-1) the one before) |
NR |
Line number across all input |
FNR |
Line number within the current file |
FILENAME |
Name of the current file |
FS |
Input field separator (default: runs of spaces and tabs) |
OFS |
Output field separator, used between items in print a, b (default: a space) |
RS |
Input record separator (default: newline). RS="" reads paragraphs separated by blank lines |
ORS |
Output record separator (default: newline) |
ENVIRON["NAME"] |
An environment variable |
ARGC, ARGV[i]
|
The command-line arguments |
RSTART, RLENGTH
|
Set by match()
|
SUBSEP |
Joins 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
| Pattern | Selects | Result on the sample files |
|---|---|---|
NR==2 |
Line number 2 |
awk 'NR==2' t4.txt gives three four five
|
NR%2 |
Odd lines |
rng.txt: start, y, z
|
NR%2==0 |
Even lines |
rng.txt: x, end
|
NR>=2 && NR<=3 |
A range of line numbers |
rng.txt: x, y
|
/start/,/end/ |
From a matching line to the next matching line |
rng.txt: start, x, y, end
|
/regex/ |
Lines matching a regular expression |
awk '/x/{c++} END{print c+0}' rng.txt gives 1
|
!/regex/ |
Lines that do not match |
awk '!/x/' rng.txt skips x
|
$1 ~ /^t/ |
Field 1 matches |
t4.txt: three four five
|
$1 !~ /^t/ |
Field 1 does not match |
t4.txt: one two, the blank line, six
|
NF |
Non-empty lines |
t4.txt: 3 lines (the blank one is dropped) |
!NF |
Blank lines | `awk '!NF' t4.txt \ |
{% raw %}length > 8
|
Lines longer than 8 characters |
t4.txt: three four five
|
Operators
| Kind | Operators | Example 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
|
| Conditional | cond ? yes : no |
n=5; (n%2==0 ? "even" : "odd") gives odd
|
| Membership | key in array |
("k" in a) is 1, ("z" in a) is 0
|
| Logic | `&& \ | \ |
The {% raw %}** power operator works in gawk only; mawk gives a syntax error. Use ^, which works in both.
String functions
| Function | Does | Example and result |
|---|---|---|
length(s) |
Length of a string |
length("Hello, World") is 12
|
substr(s, start, len) |
Part of a string, counting from 1 |
substr(s,8) is World, substr(s,1,5) is Hello
|
index(s, t) |
Position of t in s, or 0 |
index(s,"World") is 8
|
toupper(s), tolower(s)
|
Change case |
HELLO, WORLD and hello, world
|
sub(re, repl, s) |
Replace the first match in s; returns 1 or 0 |
s="foo bar foo" becomes X bar foo
|
gsub(re, repl, s) |
Replace every match; returns the count |
s="foo bar foo" becomes X bar X (returns 2) |
match(s, re) |
Find a match; sets RSTART and RLENGTH
|
On price: 42 USD with /[0-9]+/: 8 2, and substr gives 42
|
split(s, arr, sep) |
Split into an array; returns the count |
split("a,b,c",p,",") returns 3, with p[1]="a", p[3]="c"
|
sprintf(fmt, ...) |
Format like printf, return a string |
`sprintf("%05.1f\ |
{% raw %}
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
| Function | Result |
|---|---|
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 %
| Format | Meaning |
|---|---|
%d |
Integer (42.9 prints as 42) |
%5.2f |
Decimal number, width 5, 2 digits after the point |
%-6s |
String, width 6, left-aligned |
%05d |
Integer padded with zeros to width 5 |
%x, %o
|
Hexadecimal, octal |
%e |
Scientific notation |
%c |
Character 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
| Statement | Effect |
|---|---|
next |
Stop with this line and go to the next one. NR==2{next} {print} skips line 2 |
nextfile |
Skip the rest of the current file. FNR==2{nextfile} {print FILENAME": "$0} printed one line from each of two files |
exit n |
Stop 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
| Task | Code |
|---|---|
| Set and read | a["key"]=1; print a["key"] |
| Test for a key |
if ("key" in a) ... (does not create the key) |
| Count the entries |
length(a) (works in gawk and mawk) |
| Remove one key / all keys |
delete a["key"] / delete a
|
| Loop over keys |
for (k in a) print k, a[k] (order is not guaranteed, pipe to sort) |
| Split into an array |
n=split("c a b", w) gives w[1]="c", w[2]="a", w[3]="b"
|
| Two-part key |
a[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
| Task | Code |
|---|---|
| Write to a file | print $1 > "out.txt" |
| Pipe to a command | `print $1 \ |
| Write to standard error | {% raw %}print "message" > "/dev/stderr"
|
| Run a shell command | system("echo from shell") |
| Read a command's output | `"echo hello" \ |
| Read a file line by line | {% raw %}while ((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
| Goal | Command |
|---|---|
| Number the lines |
awk '{print NR": "$0}' rng.txt (first line: 1: start) |
| Print line 3 |
awk 'NR==3' rng.txt (y) |
| Print lines 2 to 3 | awk 'NR>=2 && NR<=3' rng.txt |
| Print everything after line n |
awk -v n=2 'NR>n' rng.txt (y, end, z) |
| Print the last line |
awk 'END{print}' rng.txt (z) |
| Print every other line |
awk 'NR%2' rng.txt (start, y, z) |
| Delete blank lines | awk 'NF' t4.txt |
| Remove duplicate lines | awk '!seen[$0]++' file |
| Reverse the lines |
awk '{a[NR]=$0} END{for(i=NR;i>0;i--) print a[i]}' rng.txt (starts z, end, y) |
| Join lines with commas |
awk '{printf "%s%s", sep, $0; sep=","} END{print ""}' rng.txt (start,x,y,end,z) |
| Trim leading and trailing space | `awk '{gsub(/^[ \t]+\ |
| Find the longest line | {% raw %}awk '{if (length > max) {max=length; line=$0}} END{print max, line}' t4.txt (15 three four five) |
| Count lines and words |
awk '{w+=NF} END{print NR, w}' t4.txt (4 6) |
| Swap the first two fields | `echo 'a b c' \ |
| Capitalise the first word | {% raw %}`echo 'hello world' \ |
| Print all but the first field | {% raw %}`echo 'a b c' \ |
| Count matching lines | {% raw %}awk '/x/{c++} END{print c+0}' rng.txt (1) |
| Sum a column |
awk '{s+=$1} END{print s}' file (see summing a column) |
| Print a column |
awk '{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.
| Feature | gawk | mawk 1.3.4 |
|---|---|---|
gensub() |
Works | Error: function never defined |
asort(), asorti()
|
Work | Error: function never defined |
** power operator |
Works | Syntax error |
IGNORECASE=1 |
Case-insensitive matching | Silently ignored |
PROCINFO["sorted_in"] |
Sorts for (k in a)
|
Silently ignored (loop order differs) |
BEGINFILE, ENDFILE
|
Run per file | Silently do nothing |
FIELDWIDTHS, FPAT
|
Work | Silently ignored |
| UTF-8 characters | With a UTF-8 locale, length("héllo") is 5
|
6 (counts bytes), even in a UTF-8 locale |
| Division by zero | Fatal error | Prints -nan and carries on |
0x1A + 0 |
0 |
26 |
-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 > 5also matches a header likeqty. AddNR>1 &&. -
Array loop order.
for (k in a)has no guaranteed order. Pipe tosort. -
Setting
FSinside a rule. It only affects the next line. Set it inBEGINor use-F. -
$(NF-1)on a blank line. It stops AWK with an error. Guard withNF>1. -
A string where you meant a regex.
gsub(".", ...)matches any character.
Originally published on DEV Talk.
Top comments (0)