Home / Alt manpages / column(1)

  • column(1)
  • User command
  • linux

Turn Messy Command Output into Reliable Tables with column

Ragged command output is hard to read at 2am, and column tidies it into columns or a named table. This guide covers plain lists, named and aligned tables, JSON and trees, and dodges the two defaults that cause most surprises: columns are filled before rows, and empty input fields are treated differently from missing ones. It takes about ten minutes.

  • You need: a shell and the bsdextrautils package on this Ubuntu machine.
  • Versions: the installed executable reports util-linux 2.41.3, but the local manual page is labelled util-linux 2.39.3. Verify unusual behaviour against the command on the host where a script will run.
  • Privileges: none. These examples only read input and write standard output, so no 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 the available output width. Empty lines are ignored by default. You can name a file instead of using a pipe:

$ column /path/to/items.txt

Checkpoint

Run column --version. On this machine it prints column from util-linux 2.41.3. If yours differs, keep the input small and compare the local column(1) manual before putting the command in a script.

2. Fill rows first when reading across is clearer

--fillrows, also written -x, fills each row before starting the next column. It suits short menus or labels that the reader scans 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 own width, while non-interactive output defaults to 80 columns. Output longer than a requested width is not truncated unless you ask for truncation in table mode.

3. Build a named table from records

Use --table when each input line holds fields. Add --table-columns to name the fields and create the header. Do not put a header line in the input unless you want it shown 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. For 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

Tip

The backslash continues the shell command and is not part of the data. Keep field names free of spaces, because they form a comma-separated option argument.

4. Parse a delimiter without losing empty fields

For input split on 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 sit side by side: do not swap this test for a whitespace pipeline and assume the empty value will survive.

Checkpoint

Look at the rendered line for var. There should be a blank field between the two output separators. If a later tool must tell blank from absent data, test that tool separately, because visual alignment 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. When writing to files, the manual recommends unlimited (or 0) so an accidental terminal size cannot constrain the table:

$ 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 system cleanup may remove it. If a long cell must fit a fixed display, use --table-truncate with the relevant column, and 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

Warning

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

--json requires --table-columns. Add --table-name 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"
      }
   ]
}

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 only a view of the relationships. According to the manual, circular dependencies and other anomalies are silently ignored, so a successful display does not prove the input graph is valid. Validate IDs and parent references separately when the data matters.

Common traps

  • Header appears twice. Remove the header from the input, or stop passing --table-columns. The option creates the named header itself.
  • Fields shift. Check the real delimiter and quote your separator argument.
  • A line disappears. Empty lines are ignored unless you add --keep-empty-lines or -L.

Warning

Do not overwrite a useful report with plain shell redirection. The > operator truncates its destination before column runs. Write to a new temporary path, inspect it, and replace the old file only if that is intended:

$ 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

Recovery

If the command fails, the old report is untouched. Remove the temporary file with rm /tmp/report.new only after checking its exact path, because that deletion cannot be undone through column.

Done means

  • Filling chosen. You can arrange a list and pick column-first or row-first filling deliberately.
  • Tables named. You can name table columns, align numeric-looking fields and set an output separator.
  • Empty fields kept. You preserve delimiter-separated empty fields and know whitespace parsing is a different case.
  • Width explicit. You set output width when reproducibility matters, and truncate only display-safe fields.
  • Structured output checked. You use JSON and tree output only after verifying the input field order and relationships.
  • Reports replaced safely. You write to a new path before moving a report into place.