The most common reason an AWK command prints the wrong thing is that AWK split the line somewhere you did not expect. The rule that decides where to split is the field separator. Get it right and $1, $2 and $NF point at the columns you meant. This guide covers the default behaviour, single and multiple delimiters, the four ways to set the separator, and a few traps, with sample input and the exact output of each example.
The quick answer
awk -F, '{print $2}' file.csv # comma
awk -F: '{print $1}' /etc/passwd # colon
awk -F'\t' '{print $2}' file.tsv # tab
awk -F'[,;]' '{print $2}' file.txt # comma OR semicolon
awk -F', ' '{print $2}' file.txt # the two-character separator ", "
If you are new to AWK, start with the beginner's guide. To see how column numbers work once the line is split, read how to print a column with AWK.
Sample files
Create these once. Every example below uses them.
printf 'root:x:0:0:root:/root:/bin/bash\nada:x:1000:1000:Ada L:/home/ada:/bin/zsh\n' > passwd.txt
printf 'id\tname\tcity\n1\tAda Lovelace\tLondon\n2\tAlan Turing\tWilmslow\n' > t.tsv
printf 'a, b;c ,d\n' > mix.txt
printf 'a,,c,\n' > empty.csv
printf ' one two\tthree \n' > ws.txt
printf '2026-10-01 ERROR disk full\n2026-10-02 INFO ok\n' > app.log
cat > sales.csv <<'EOF'
date,region,product,qty,price
2026-10-01,north,pen,10,1.50
2026-10-01,south,book,3,12.00
2026-10-02,north,book,2,12.00
2026-10-02,east,pen,25,1.50
2026-10-03,south,pen,5,1.50
EOF
cat > access.log <<'EOF'
10.0.0.1 - - [24/Oct/2026:10:00:01 +0000] "GET /index.php HTTP/1.1" 200 512
10.0.0.2 - - [24/Oct/2026:10:00:02 +0000] "GET /about HTTP/1.1" 404 128
10.0.0.1 - - [24/Oct/2026:10:00:03 +0000] "POST /login HTTP/1.1" 302 0
EOF
What AWK does when you set nothing
By default, fields are separated by runs of spaces and tabs, and leading and trailing whitespace is ignored. The file ws.txt has two leading spaces, three spaces between words, a tab, and trailing spaces:
awk '{print NF, "[" $1 "]"}' ws.txt
# 3 [one]
Three fields, and the first one is clean. That is why plain awk '{print $2}' works well on column-aligned output like df or ps, and why it fails on anything else.
Set a single-character separator
Use -F followed by the character. A single character other than a space is taken literally, so you do not need to escape it:
awk -F: '{print $1 " -> " $NF}' passwd.txt
# root -> /bin/bash
# ada -> /bin/zsh
printf 'id|name|city\n1|Ada|London\n' | awk -F'|' 'NR>1 {print $2}'
# Ada
echo 'a.b.c' | awk -F. '{print $2}'
# b
The pipe and the dot are special characters in regular expressions, yet both worked as plain characters here. I tested that on gawk and mawk. Quote the pipe anyway, because the shell treats an unquoted | as a pipe.
Tabs
A tab-separated file needs -F'\t'. Without it, a name like "Ada Lovelace" is split at the space:
awk -F'\t' 'NR>1 {print $2}' t.tsv
# Ada Lovelace
# Alan Turing
awk 'NR>1 {print $2}' t.tsv # default splitting
# Ada
# Alan
In bash you can also pass a real tab character with -F$'\t'; it gave the same result.
Multi-character and multiple delimiters
When the separator is more than one character, AWK treats it as a regular expression. That is how you get several delimiters at once.
| Goal | Option | Input → result |
|---|---|---|
| Exactly two characters | -F', ' |
x, y, z → $2 is y
|
| Comma or semicolon | -F'[,;]' |
a,b;c → a, b, c
|
| Any run of spaces, commas, semicolons | -F'[ ,;]+' |
a, b;c ,d → 4 fields |
| A word as the separator | `-F'one\ | two'` |
| Delimiter with optional spaces around it | -F' *, *' |
a, b ,c → a, b, c
|
For example, with mix.txt (a, b;c ,d):
awk -F'[ ,;]+' '{print NF; print $1 "|" $2 "|" $3 "|" $4}' mix.txt
# 4
# a|b|c|d
Trimming is the other common need. A plain -F, on a, b ,c leaves the spaces inside the field ($2 is " b "). Put the spaces in the pattern and they disappear:
echo 'a, b ,c' | awk -F' *, *' '{print "[" $1 "][" $2 "][" $3 "]"}'
# [a][b][c]
Empty fields and leading spaces
With a single-character separator, every delimiter counts, even next to another one. The line a,,c, has four fields, two of them empty:
awk -F, '{print NF; print "[" $2 "]"}' empty.csv
# 4
# []
Default splitting behaves differently: it collapses runs of whitespace. You can see the difference by asking for a single space as the separator:
echo 'a b' | awk '{print NF}' # 2 (default: runs collapse)
echo 'a b' | awk -F'[ ]' '{print NF}' # 3 (each space separates, so there is an empty field)
This leads to a trap. If you use a regex like [ ]+ on a line that starts with spaces, you get an empty first field, which the default splitting would have skipped:
echo ' a b' | awk '{print NF, "[" $1 "]"}' # 2 [a]
echo ' a b' | awk -F'[ ]+' '{print NF, "[" $1 "]"}' # 3 []
When you only want whitespace splitting, leave the separator alone.
Four ways to set the separator
| Way | Example | Good for |
|---|---|---|
-F option |
awk -F, '...' file |
One-liners |
-v FS= |
awk -v FS=, '...' file |
Same effect, handy in scripts |
BEGIN block |
awk 'BEGIN{FS=","} ...' file |
Program files |
| After the program | awk '...' FS=, file |
Different separator per file |
All four split sales.csv on commas when I tested them. The last form is the odd one out, because the assignment takes effect when AWK reaches it in the argument list. That lets you read files with different formats in one run:
awk '{print $1}' FS=, sales.csv FS=: passwd.txt
The first file's lines print their first comma-separated field (date, 2026-10-01, ...) and then, once AWK reaches the second assignment, passwd.txt lines print their first colon-separated field (root, ada).
Gotcha: changing FS inside a rule is one line too late
AWK splits a line into fields as soon as it reads it. If you set FS in the main rule, the current line has already been split, so the change applies from the next line:
awk '{FS=":"; print $1}' passwd.txt
# root:x:0:0:root:/root:/bin/bash (first line: still split on whitespace)
# ada
Set it in BEGIN instead, or force a re-split with $0=$0:
awk '{FS=":"; $0=$0; print $1}' passwd.txt
# root
# ada
FS reads, OFS writes
FS controls how input is split. OFS controls what goes between fields when you print with commas. Combine them to convert formats. Assigning to a field (even to itself, with $1=$1) makes AWK rebuild the line using OFS:
awk 'BEGIN{FS=","; OFS=";"} {$1=$1; print}' sales.csv | head -2
# date;region;product;qty;price
# 2026-10-01;north;pen;10;1.50
awk -F, -v OFS='\t' '{$1=$1} 1' sales.csv | head -2 | cat -A
# date^Iregion^Iproduct^Iqty^Iprice$ (^I is a tab, $ marks the line end)
# 2026-10-01^Inorth^Ipen^I10^I1.50$
The trailing 1 is a pattern that is always true, so AWK prints the line.
Real examples
A timestamp inside square brackets
In a web server log, the date sits between [ and ]. Use both brackets as separators. In a bracket expression, put ] first so it counts as a character:
awk -F'[][]' '{print $2}' access.log
# 24/Oct/2026:10:00:01 +0000
# 24/Oct/2026:10:00:02 +0000
# 24/Oct/2026:10:00:03 +0000
The quoted request line
Splitting on the double quote isolates the request. Then split() breaks it into method and path:
awk -F'"' '{split($2, r, " "); print r[1], r[2]}' access.log
# GET /index.php
# GET /about
# POST /login
A word as the separator
awk -F' ERROR ' 'NF>1 {print $2}' app.log
# disk full
Only the first line contains ERROR, so it is the only one with more than one field.
Split into characters, or into fixed widths
An empty separator splits every character into its own field. This worked on both gawk and mawk:
echo abc | awk -v FS= '{print NF, $2}'
# 3 b
For fixed-width columns, gawk has FIELDWIDTHS. This is a gawk-only feature, and the failure on other awks is quiet. Create a one-line file whose first 10 characters are a date and next 5 are a level:
printf '2026-10-01ERROR\n' > fixed.txt
awk 'BEGIN{FIELDWIDTHS="10 5"} {print "[" $1 "][" $2 "]"}' fixed.txt
# gawk: [2026-10-01][ERROR]
# mawk: [2026-10-01ERROR][] (setting ignored, no error message)
If a script must run on mawk too, use substr($0, 1, 10) and substr($0, 11, 5) instead.
When a separator is not enough: quoted CSV
A separator cannot understand quoting. On the line Ada,"hi, there", -F, gives you three fields, because the comma inside the quotes also splits. For real CSV with quoted fields you need FPAT in gawk, or the built-in CSV mode that gawk added in version 5.3.0. I could only test gawk 5.2.1 here, so I have not run the CSV mode. The CSV article in this series covers it.
Quick reference
| Input format | Option |
|---|---|
| Spaces and tabs (default) | nothing |
| Comma | -F, |
| Colon | -F: |
| Pipe | `-F'\ |
| Tab | {% raw %}-F'\t'
|
| Either of several characters | -F'[,;]' |
| Delimiter with spaces around it | -F' *, *' |
| Square brackets | -F'[][]' |
| Each character | -v FS= |
Frequently asked questions
How do I use multiple delimiters in AWK?
Give -F a regular expression: -F'[,;]' for a comma or a semicolon, or -F'one|two' for two words. Add a + after the brackets to treat a run of them as one separator.
What is the difference between -F and FS?
Nothing for your program. -F, on the command line sets the same FS variable that BEGIN{FS=","} sets.
Why does $1 print the whole line?
Because the separator never matched. If you set -F, but the file uses semicolons, there is only one field, and $1 is the whole line. Check with awk -F, '{print NF}' file.
How do I set the separator to a tab?
Use -F'\t', or in bash -F$'\t'
Originally published on DEV Talk.
Top comments (0)