← back to latest

Line up a CSV into readable columns right in the terminal

column -s, -t sales.csv

anatomy

column
Arrange input into columns. Without -t it packs a list into newspaper-style columns like ls does. With -t it treats each line as a row and each field as a cell.
-s,
Split fields on commas instead of runs of whitespace. The argument is a set of characters, not a string: -s ',;' splits on either one.
-t
Table mode. column reads every line first, measures the widest cell in each column, then pads every cell to that width. That full read is why it waits for EOF before printing anything.
sales.csv
The input file. Leave it off and column reads stdin, which is how it ends up at the tail of most pipelines.

Sample output

date        region     product   units  revenue
2026-01-15  northeast  widget-a  150    4500.00
2026-01-15  southwest  widget-b  89     2670.00
2026-02-01  northeast  widget-a  200    6000.00
2026-02-01  midwest    widget-c  45     2250.00
2026-03-10  southwest  widget-a  175    5250.00
2026-03-10  northeast  widget-b  120    3600.00
2026-04-22  midwest    widget-a  95     2850.00
2026-04-22  southwest  widget-c  60     3000.00

Every column is as wide as its longest cell plus two spaces, so you can read down revenue without counting commas. The header row gets no special treatment. It lines up because it happens to be the first row.

When you would reach for it

A CSV export you want to eyeball before you decide what to do with it, a TSV from a database dump, or any command whose output is ragged because its fields vary in length. Pipe it into column -t and the structure becomes visible. It is also the last step in a lot of pipelines: sort, awk, or cut does the work, and column makes the result legible.

Gotchas

  • On macOS the BSD column collapses adjacent delimiters, so a row like 2026-05-01,,widget-a loses its empty cell and every field after it shifts one column left. The util-linux column on Debian keeps empty cells in place. On a Mac, fill the gaps first: sed -e 's/,,/, ,/g; s/,,/, ,/g' file.csv | column -s, -t. The substitution runs twice because each match consumes the comma that the next empty field would need.
  • -s takes a set of characters, not a multi-character separator. -s ', ' splits on commas and on spaces, which shreds any cell that contains a space.
  • column knows nothing about CSV quoting. A cell like "Portland, OR" splits at the comma into two cells. For real CSV with quoted fields, use mlr --icsv --opprint cat file.csv.
  • Without -s, the delimiter is any run of whitespace. That suits TSV files and command output, but a cell like Ada Lovelace becomes two cells. On slim Debian images column may be missing entirely: install bsdextrautils.

Variants

$ (head -1 sales.csv; tail -n +2 sales.csv | sort -t, -k5 -nr) | column -s, -t

Sort by revenue and keep the header on top. The parentheses run both commands in one subshell, so their output reaches column as a single stream

$ column -s, -t -R 4,5 sales.csv

Linux only. Right-aligns the numeric columns so the digits line up by place value

$ column -s, -t -J -n sales -N date,region,product,units,revenue <(tail -n +2 sales.csv)

Linux only. Turns the CSV into JSON, with every value as a string. -N names the columns, and the process substitution feeds in the file without its header row

lineage

column first appeared in 4.3BSD-Reno around 1990, a small utility for packing lists into the width of a terminal. Table mode came along with it, and for decades that was the whole tool. Linux distributions carried the BSD code for years, Debian in its bsdmainutils package, until util-linux adopted column and Karel Zak rebuilt it on top of libsmartcols in 2017. The rewrite is why the Linux version grew JSON output, right alignment, column names, and truncation, while the macOS version still behaves much as it did in 1990.