DEV Community

Cover image for AWK Cheat Sheet: Commands, Variables, Functions & One-Liners
devTalk
devTalk

Posted on Originally published at dev-talk.com

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

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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 %
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

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 > 5 also matches a header like qty. Add NR>1 &&.
  • Array loop order. for (k in a) has no guaranteed order. Pipe to sort.
  • SettingFSinside 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.

Originally published on DEV Talk.

Top comments (0)