Turn Messy Command Output into Reliable Tables with column
You will use column to turn whitespace- or delimiter-separated input into readable columns, then build a named table with predictable alignment. The examples also show how to avoid the two defaults that cause most surprises: columns are filled before rows, and empty input fields are normally treated differently from missing fields.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Arrange a simple list
- 2. Choose row-first filling when reading across is clearer
- 3. Build a named table from records
- 4. Parse a delimiter without destroying empty fields
- 5. Control width and decide whether truncation is safe
- 6. Export a table as JSON when names are available
- 7. Print a tree from parent and child IDs
- Common mistakes and safe recovery
Allow about ten minutes. You need a shell and the bsdextrautils package on this Ubuntu machine. The installed executable reports util-linux 2.41.3; the local manual page is labelled util-linux 2.39.3, so verify unusual behaviour against the command on the host where a script will run. These examples only read input and write standard output. They do not need sudo.
1. Arrange a simple list
Give column one item per line. With no options, it reads standard input and fills columns before rows:
$ printf '%s\n' pear apple banana kiwi | column
pear apple banana kiwi
The exact gaps depend on the item widths and available output width. Empty lines are ignored by default. A file can be supplied instead of the pipe:
$ column /path/to/items.txt
Checkpoint: run column --version. On this machine it prints column from util-linux 2.41.3. If your output differs, keep the input small and compare the local column(1) manual before putting the command in a script.
2. Choose row-first filling when reading across is clearer
--fillrows, also written -x, fills each row before starting the next column. It is useful for short menus or labels where the reader should scan left to right:
$ printf '%s\n' pear apple banana kiwi | column --fillrows --output-width 20
pear apple
banana kiwi
The width option makes the example reproducible. Without it, an interactive terminal supplies its width, while non-interactive output defaults to 80 columns. Output longer than a requested width is not truncated unless you request truncation in table mode.
3. Build a named table from records
Use --table when each input line contains fields. Add --table-columns to name the fields and create the header. Do not include a header line in the input unless you deliberately want that line displayed as data:
$ printf '%s\n' 'root 20G online' 'var 8G full' |
column --table --table-columns NAME,SIZE,STATE --table-right SIZE
NAME SIZE STATE
root 20G online
var 8G full
--table-right SIZE right-aligns the named column. The default output separator is two spaces. To use a visible separator, set --output-separator:
$ printf '%s\n' 'root 20G online' 'var 8G full' |
column --table --table-columns NAME,SIZE,STATE --table-right SIZE \
--output-separator ' | '
NAME | SIZE | STATE
root | 20G | online
var | 8G | full
The backslash continues the shell command; it is not part of the data. Keep field names free of spaces because they are a comma-separated option argument.
4. Parse a delimiter without destroying empty fields
For input separated by a known character, use --separator or -s. This example keeps the empty middle field between the two colons:
$ printf '%s\n' 'root:20G:online' 'var::full' |
column --table --separator ':' --output-separator ' | '
root | 20G | online
var | | full
In table mode without explicit names, the command uses the input's fields but does not invent a header. The installed command treats -s as a non-greedy separator, as documented since util-linux 2.23. That matters when two delimiters are adjacent: do not replace this test with a whitespace pipeline and assume the empty value will survive.
Checkpoint: inspect the rendered line for var. There should be a blank field between the two output separators. If a later tool needs to distinguish blank from absent data, test that tool separately; visual alignment alone is not a data-quality check.
5. Control width and decide whether truncation is safe
Use --output-width or -c when output goes to a terminal, log or fixture with a known width. The manual recommends unlimited (or 0) when writing to files so the table is not constrained by an accidental terminal size:
$ printf '%s\n' 'root 20G online' 'var 8G full' |
column --table --table-columns NAME,SIZE,STATE --output-width unlimited \
> /tmp/storage-table.txt
$ sed -n '1,4p' /tmp/storage-table.txt
NAME SIZE STATE
root 20G online
var 8G full
Redirecting to /tmp is ordinary user work. The file is temporary and may be removed by system cleanup. If a long cell must fit a fixed display, use --table-truncate with the relevant column, but treat the result as presentation only:
$ printf '%s\n' 'alice short' 'bob very-long-description' |
column --table --output-width 20 --table-truncate 2
name description
alice short
bob very-long-des
Do not truncate identifiers, paths or values that another command will parse. A shorter display is not a shorter value.
6. Export a table as JSON when names are available
--json requires --table-columns. Supply --table-name as well when the JSON key should be explicit:
$ printf '%s\n' 'alpha 7' 'beta 9' |
column --json --table --table-columns name,count --table-name items
{
"items": [
{
"name": "alpha",
"count": "7"
},{
"name": "beta",
"count": "9"
}
]
}
The values are strings unless you define a column with a json=number or json=boolean attribute using --table-column. JSON output is a format conversion, not validation: check that the source fields are in the expected order and that blank values are acceptable to the consumer.
7. Print a tree from parent and child IDs
For records with an ID, a parent ID and a label, use --tree-id, --tree-parent and --tree. Column numbers are one-based:
$ printf '%s\n' '1 0 root' '2 1 child' '3 1 other' '4 2 leaf' |
column --tree-id 1 --tree-parent 2 --tree 3
1 0 root
2 1 ├─child
4 2 │ └─leaf
3 1 └─other
The tree is an output view of the relationships. Circular dependencies and other anomalies are silently ignored according to the manual, so do not use a successful display as proof that the input graph is valid. Validate IDs and parent references separately when the data matters.
Common mistakes and safe recovery
If a header appears twice, remove the header from the input or stop supplying --table-columns; the option itself creates the named header. If fields shift, check the real delimiter and quote your separator argument. If a line disappears, remember that empty lines are ignored unless you add --keep-empty-lines or -L.
Do not overwrite a useful report blindly with shell redirection. The > operator truncates its destination before column runs. Write to a new temporary path, inspect it, then replace the old file only if that replacement is intentional:
$ column --table --table-columns NAME,SIZE,STATE /path/to/input.txt > /tmp/report.new
$ test -s /tmp/report.new && mv /tmp/report.new /path/to/report.txt
If the command fails, the old report is untouched. Remove the temporary file with rm /tmp/report.new only after checking its exact path; that deletion cannot be undone through column.
Done means
- You can arrange a list and choose column-first or row-first filling deliberately.
- You can name table columns, align numeric-looking fields and choose an output separator.
- You preserve delimiter-separated empty fields and know that whitespace parsing is a different case.
- You set output width explicitly when reproducibility matters, and truncate only display-safe fields.
- You use JSON and tree output only after checking the input field order and relationships.
- You write replacement reports to a new path before moving them into place.