📊 column — Format Output Into Columns

What is column?

The column utility takes messy, unaligned text and arranges it into neat columns. Feed it a list or delimited data and it calculates optimal widths automatically — no manual field-width arithmetic needed. It is the fastest way to turn raw command output into something readable at a glance.

Two main modes: table mode (-t) takes delimited input and aligns it into columns, and fill mode arranges items to fit the terminal width. The util-linux version (standard on RHEL/Ubuntu) has additional flags for headers, separators, and right-alignment.

Syntax:
column [OPTIONS] [FILE ...]

column -t                  # table mode — auto-align whitespace-delimited input
column -t -s ':'           # use colon as delimiter
column -t -s $'\t'         # tab-delimited input
column -t -N "H1,H2,H3"   # add header row (util-linux 2.30+)
column -c 80              # fill mode — wrap to 80 columns

Examples

1
Basic Table Alignment with -t

The most common use — take whitespace-separated data and align it:

# Without column — hard to read
echo "NAME STATUS PID MEMORY
nginx running 1234 45MB
sshd running 891 12MB
postgresql running 2201 380MB
redis stopped - -" | cat

# With column -t — instant alignment
echo "NAME STATUS PID MEMORY
nginx running 1234 45MB
sshd running 891 12MB
postgresql running 2201 380MB
redis stopped - -" | column -t
Without column:
NAME STATUS PID MEMORY nginx running 1234 45MB sshd running 891 12MB postgresql running 2201 380MB redis stopped - -
With column -t:
NAME STATUS PID MEMORY nginx running 1234 45MB sshd running 891 12MB postgresql running 2201 380MB redis stopped - -
💡 One flag, big payoff: Piping almost any tabular output through column -t instantly makes it readable. No format strings, no field widths — column calculates everything from the data.
2
Custom Delimiter with -s

Process colon-delimited, CSV, or tab-separated data:

# Format /etc/passwd nicely
cut -d: -f1,3,6,7 /etc/passwd | column -t -s ':'

# Format a CSV (simple — no quoted fields)
echo "hostname,ip,role,os
web01,192.168.1.10,nginx,Ubuntu22
db01,192.168.1.20,postgresql,RHEL9
cache01,192.168.1.30,redis,Ubuntu22" | column -t -s ','

# Tab-delimited — from a script or spreadsheet export
printf "Server\tCPU\tRAM\tDisk\n" > /tmp/inv.tsv
printf "web01\t4\t16GB\t500GB\n" >> /tmp/inv.tsv
printf "db01\t16\t64GB\t2TB\n" >> /tmp/inv.tsv
column -t -s $'\t' /tmp/inv.tsv
CSV output:
hostname ip role os web01 192.168.1.10 nginx Ubuntu22 db01 192.168.1.20 postgresql RHEL9 cache01 192.168.1.30 redis Ubuntu22
Tab delimiter: Use $'\t' (ANSI-C quoting) to pass a literal tab to -s. Plain "\t" in double quotes does not expand to a tab in all shells.
3
Adding Headers with -N (util-linux 2.30+)

Inject a header row without it being in the data stream:

# Add headers to ps output
ps aux --no-headers | awk '{print $1,$2,$3,$11}' | \
    column -t -N "USER,PID,CPU%,COMMAND"

# Add headers to a data file that has none
column -t -s ',' -N "HOST,IP,ROLE,OS" /tmp/servers.csv

# Right-align numeric columns with -R (util-linux 2.32+)
echo "server cpu mem disk
web01 4 16 500
db01 16 64 2000
cache01 2 8 100" | column -t -R 2,3,4
With headers and right-aligned numbers:
server cpu mem disk web01 4 16 500 db01 16 64 2000 cache01 2 8 100
Version check: The -N and -R flags require util-linux 2.30+ and 2.32+ respectively. Check your version with column --version. Older systems (RHEL 7, Ubuntu 18.04) may not have these flags — use printf for headers on older systems.
4
Fill Mode — Arrange Items Across the Terminal

Without -t, column fills the terminal width with items arranged in columns — like ls does:

# List items across terminal width
ls /etc | column

# Force a specific width
ls /etc | column -c 60

# Display a list of packages in columns
rpm -qa --queryformat "%{NAME}\n" | sort | column

# Show available bash builtins in columns
compgen -b | column
compgen -b | column output:
. : [ alias bg bind break builtin caller cd command compgen complete compopt continue declare dirs disown echo enable eval exec exit export false fc fg getopts hash help history jobs kill let local logout mapfile popd printf pushd pwd read readarray readonly return set shift shopt source suspend test times trap true type typeset ulimit umask unalias unset wait
💡 -x flag: By default fill mode fills down then across. Add -x to fill across then down — like reading order.
5
Beautifying Command Output

Pipe common commands through column for instant readability:

# Mount points — cleaner than raw mount output
mount | grep -v "cgroup\|proc\|sys\|dev\|run" | \
    awk '{print $1, $3, $5}' | column -t

# Environment variables as a table
env | sort | column -t -s '='

# Listening ports — cleaner ss output
ss -tlnp | column -t

# /etc/hosts as aligned table
grep -v "^#\|^$" /etc/hosts | column -t

# crontab entries aligned
crontab -l | grep -v "^#" | column -t
/etc/hosts example:
127.0.0.1 localhost 127.0.1.1 bambustudio bambustudio.local 192.168.1.1 gateway router 192.168.1.10 web01 web01.internal
6
Building Formatted Reports in Scripts
#!/bin/bash
# Server inventory report using column

TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT

# Build data into temp file — header first
echo "HOSTNAME IP_ADDRESS CPU_CORES RAM_GB DISK_GB OS_VERSION" > "$TMPFILE"
echo "-------- ---------- --------- ------ ------- ----------" >> "$TMPFILE"

for host in web01 web02 db01 cache01; do
    # In real life these would be ssh calls
    printf "%s %s %s %s %s %s\n" \
        "$host" \
        "192.168.1.$(shuf -i 10-50 -n1)" \
        "$(shuf -i 2-16 -n1)" \
        "$(shuf -i 4-64 -n1)" \
        "$(shuf -i 100-2000 -n1)" \
        "RHEL9" >> "$TMPFILE"
done

# Format and display
echo ""
echo "=== Server Inventory ==="
echo ""
column -t "$TMPFILE"
echo ""
Output:
=== Server Inventory === HOSTNAME IP_ADDRESS CPU_CORES RAM_GB DISK_GB OS_VERSION -------- ---------- --------- ------ ------- ---------- web01 192.168.1.23 8 32 500 RHEL9 web02 192.168.1.41 4 16 500 RHEL9 db01 192.168.1.15 16 64 2000 RHEL9 cache01 192.168.1.38 2 8 100 RHEL9
7
column vs printf — Choosing the Right Tool
# column — best when data width is unknown at script write time
# Let column figure out widths from actual data
getent passwd | cut -d: -f1,3,7 | column -t -s ':'

# printf — best when you control the format and need exact widths
printf "%-20s %6s %s\n" "USERNAME" "UID" "SHELL"
printf "%-20s %6s %s\n" "--------" "---" "-----"
while IFS=: read -r user _ uid _ _ _ shell; do
    printf "%-20s %6s %s\n" "$user" "$uid" "$shell"
done < /etc/passwd

# Combine both — printf for header, column for data
printf "%-s\t%-s\t%-s\n" "USER" "UID" "SHELL"
printf "%-s\t%-s\t%-s\n" "----" "---" "-----"
cut -d: -f1,3,7 /etc/passwd | column -t -s ':'
💡 Rule of thumb: If the column widths depend on your data (hostnames, usernames, paths — variable length), use column -t. If you know the widths ahead of time and need precise control (reports with fixed-width numeric fields), use printf. They work beautifully together.
8
Separator Lines and Table Borders

Combining column with printf for polished bordered output:

#!/bin/bash
# Disk usage table with separator

SEP=$(printf '%0.s-' {1..55})

{
    echo "FILESYSTEM TOTAL USED AVAIL USE%"
    echo "$SEP"
    df -h --output=target,size,used,avail,pcent | \
        tail -n +2 | \
        grep -v "tmpfs\|udev"
} | column -t

# Hidden trick — use column on its own --table-* options (util-linux 2.30+)
# to add column separators
echo "NAME AGE CITY
Alice 34 Albany
Bob 28 Binghamton
Craig 65 Vestal" | column -t --table-columns NAME,AGE,CITY \
                              --table-right AGE
Disk usage output:
FILESYSTEM TOTAL USED AVAIL USE% ------------------------------------------------------- / 50G 22G 28G 44% /boot 500M 120M 380M 24% /home 100G 45G 55G 45% /var 30G 27G 3G 90%

Quick Reference

CommandWhat it does
cmd | column -tAuto-align whitespace-delimited output into columns
cmd | column -t -s ':'Use colon as field delimiter
cmd | column -t -s ','Use comma as delimiter (simple CSV)
cmd | column -t -s $'\t'Tab-delimited input
cmd | column -t -N "H1,H2"Add header row (util-linux 2.30+)
cmd | column -t -R 2,3Right-align columns 2 and 3 (util-linux 2.32+)
cmd | columnFill mode — arrange items across terminal width
cmd | column -c 80Fill mode — wrap to 80 columns
cmd | column -xFill across then down (reading order)
column -t file.txtRead from file instead of stdin
column --versionCheck util-linux version for feature availability

column vs printf for Output Formatting

SituationToolWhy
Variable-length data (names, paths)column -tCalculates widths from data automatically
Fixed numeric fields, precise alignmentprintfYou control exact field widths
Quick pipe to clean up command outputcolumn -tOne flag, no format string needed
Colon/CSV/tab delimited inputcolumn -t -sBuilt-in delimiter support
ANSI colors, special formattingprintfcolumn strips/misaligns with escape codes
Headers on data without headerscolumn -NInject header without touching data
⚠️ ANSI color codes and column: Escape codes for color contain invisible characters that column counts as width — causing misalignment. Apply color codes after column alignment, or use printf when color and alignment are both needed.