How to Use awk Fields Separators and Printf Step by Step
Learn awk fields and printf: $1 and NF, set the field separator with -F or FS, control the output separator, align columns and rebuild a line.

Awk is a language built around one idea: read lines, split them into fields and run code on them. Once the field model is clear, most one-liners follow. This post covers the three things to learn first: how lines are split, how to change the splitting and how to format output. It is the practical start for the grep, sed and awk guide. Examples use GNU awk 5.2 and work in mawk and the one-true-awk unless noted.
Fields and NF
printf 'ada admin 42\ngrace dev 17\n' | awk '{ print $2, $NF, NF }'admin 42 3
dev 17 3
With the default separator, awk splits on runs of spaces and tabs and ignores blanks at the ends. $NF is the last field, whatever the line length, and $(NF-1) the one before. In print $2, $NF the comma outputs the value of OFS (a space) between the two.
Other separators
awk -F: '{ print $1, $7 }' /etc/passwd | head -n 2
awk -F'[,;]' '{ print $2 }' mixed.txt
awk 'BEGIN { FS = "\t" } { print $3 }' data.tsvroot /bin/bash
daemon /usr/sbin/nologin
A single character is taken literally, except a single space, which means the default whitespace mode. Anything longer is a regular expression, so -F'[,;]' splits on a comma or a semicolon and -F', *' on a comma and any following spaces. For a tab, quote it as -F'\t'. Fields containing the separator inside quotes, as in CSV, are not handled by plain awk; see awk and CSV.
Format with printf
awk -F: '{ printf "%-16s %-22s %s\n", $1, $6, $7 }' /etc/passwd | head -n 2
awk '{ printf "%s: %6.2f%%\n", $1, $3 / 100 * 100 }' scores.txtroot /root /bin/bash
daemon /usr/sbin /usr/sbin/nologin
%-16s pads a string to 16 characters on the right, %6.2f prints a number with two decimals in a field of six, and %% prints a percent sign. Unlike print, printf adds no newline and no separators. Matching the number of formats to the number of arguments avoids silent blanks.
Rebuild a line
echo 'a b c' | awk '{ $1 = $1 } 1'
echo 'a b c' | awk 'BEGIN { OFS = "," } { $1 = $1 } 1'a b c
a,b,c
Assigning to any field makes awk rebuild $0 by joining the fields with OFS. The assignment $1 = $1 changes nothing in value but forces the rebuild, so it normalizes whitespace or changes the separator. The pattern 1 is true for every line and prints it. Changing NF also rebuilds, and NF = 2 drops everything after the second field.
Where this does not apply
Fixed-width data is better sliced with substr($0, start, len) or cut -c. Text with embedded separators needs a real parser. awk compares fields as strings unless they look numeric, so add +0 to force a number: $3 + 0 > 10. To process numbers in columns, see sums and averages, and for counting with arrays, associative arrays.
How awk splits a line
By default awk splits each record into fields on runs of blanks and tabs and ignores leading and trailing blanks, so the line alpha beta has two fields. Setting FS to a single space is the same default, which is a special case, while any other single character is taken literally and a longer string is treated as a regular expression. -F, splits on commas and gives an empty field between two adjacent commas. -F'\t' splits on tabs, which is the right choice for data that has spaces inside fields. The special variable NF holds the number of fields, so $NF is the last and $(NF-1) the one before it. The parentheses matter, because $NF-1 means the last field minus one, a number, and awk does the arithmetic without complaint. This difference between a field reference and a computed number accounts for a large share of awk puzzles.
The assignment to FS inside a rule takes effect on the next line, not the current one, since the current record is already split. Set it in a BEGIN block, or pass it with -F.
Changing fields and the output separator
Assigning to a field rebuilds the whole record, joining the fields with OFS, the output field separator, which is a single space by default. So { $2 = "X"; print } prints the line with fields separated by single spaces, even if the input used tabs or several spaces. That is useful for normalizing, with $1 = $1 as the trick to force a rebuild with the current separator, and surprising when you wanted to preserve the layout. To keep the original text, edit with sub or gsub on $0 instead of assigning to fields. Setting NF to a smaller number drops the trailing fields, which is a short way to cut columns, and in portable code it is better to print the fields you want than to rely on that behavior.
Set OFS in BEGIN so the output of a comma-separated file stays comma-separated: BEGIN { FS = OFS = "," }. Forgetting this is the usual reason a CSV edit comes out with spaces.
Formatted output and where awk stops
printf in awk works like the C function. Use %-12s for left-aligned text in a fixed width, %8.2f for a number with two decimals and %d for an integer, and add the newline yourself, since printf does not. A value that is too wide for the field pushes the rest of the line over, so choose widths from the data or compute them in a first pass. Remember that %d truncates, and very large integers may print in exponent form or lose precision on awk implementations that use floating-point numbers for everything, so use a format like %.0f for big counts. Awk has no notion of quoted fields, so a CSV with commas inside quotes needs a different approach: GNU awk has FPAT to describe what a field looks like, and version 5.3 added a --csv mode, while other implementations need a CSV-aware tool.
Debugging a field problem quickly
When a field is not what you expect, print the record with its field count and each field in brackets: { printf "%d:", NF; for (f = 1; f <= NF; f++) printf "[%s]", $f; print "" }. The brackets show leading and trailing spaces, and the count shows whether the separator is what you assumed. Look for a carriage return at the end of the last field with cat -A on the input, which shows tabs as ^I and line endings as $. The most common faults are a separator that is a tab and not a space, repeated separators that produce empty fields when the separator is a single character, and a header row that changes the column count. Fix the separator first and the rest tends to fall into place.
When several awk programs grow similar, put the common setup in a file and load it with -f, together with the program for the specific job. Two -f options are concatenated, which gives you a small library without any extra tooling.
See the gawk manual on fields and POSIX awk.


