DEV Community

Cover image for AWK Field Separator: -F, FS and Multiple Delimiters
devTalk
devTalk

Posted on Originally published at dev-talk.com

AWK Field Separator: -F, FS and Multiple Delimiters

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

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

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

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

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

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

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

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

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

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

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

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

Set it in BEGIN instead, or force a re-split with $0=$0:

awk '{FS=":"; $0=$0; print $1}' passwd.txt
# root
# ada
Enter fullscreen mode Exit fullscreen mode

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

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

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

A word as the separator

awk -F' ERROR ' 'NF>1 {print $2}' app.log
# disk full
Enter fullscreen mode Exit fullscreen mode

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

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

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)