Home / Alt manpages / expand(1)

  • expand(1)
  • User command
  • linux

Turn Tabs into Predictable Spaces with expand

You will convert tab characters to spaces on standard output, choose the tab-stop layout when needed, and leave the original file untouched. The examples use GNU coreutils 9.4, installed here as package version 9.4-3ubuntu6.3. Allow about five minutes for a first pass, or longer if you need to choose a layout for a format with strict column rules.

Before you start

You need a shell and a text file containing tabs. No elevated privileges are needed. expand reads files and writes converted text to standard output; it does not have an in-place editing option. That default is useful: you can inspect the result or redirect it to a new file before replacing anything.

Use a disposable working copy if you are unsure what a file contains. The command changes no input file by itself, but a redirection such as expand input.txt > input.txt can truncate the input before expand reads it. Never use that form.

Checkpoint 1: convert a file using the default layout

Run expand with the input file as its argument. By default, tab stops are eight characters apart.

expand notes.txt

The converted text appears in your terminal. To save it without touching the source, redirect it to a separate output file:

expand notes.txt > notes-spaces.txt

For a quick check, ask the shell to show invisible characters in both files. GNU sed displays tabs as ^I with this command:

sed -n '1,12l' notes.txt
sed -n '1,12l' notes-spaces.txt

In the first output, tab characters may appear as \t or ^I, depending on the display command. In the second, the equivalent columns are made from spaces. The exact number of spaces depends on the current column and the selected tab stops, not simply on replacing every tab with eight spaces.

Checkpoint 2: use standard input in a pipeline

With no file argument, or with -, expand reads standard input. This makes it suitable between commands. Here, the first command creates two tab-separated fields and expand prints them with the default eight-column stops:

printf 'name\tstatus\nweb\tready\n' | expand

Expected output:

name    status
web     ready

The spaces above are significant. They align the second field at the next tab stop after the text already printed on that line. A tab is a movement to a stop, not a fixed-width character.

Choose a regular tab width

Use -t N or its long form --tabs=N when stops should be N characters apart instead of eight. This is useful when producing a human-readable report whose columns are intentionally compact.

printf 'item\tcount\nalpha\t3\nbeta\t12\n' | expand --tabs=12

Expected output:

item        count
alpha       3
beta        12

Do not infer that every tab becomes twelve spaces. In the first line, four spaces follow item because the next stop is column 12. If a field is already near or beyond a stop, the number changes accordingly.

Checkpoint 3: describe irregular tab stops

Some text has a short first field and wider later fields. Pass a comma-separated list to -t, such as 4,10,20. These are explicit tab positions, counted from the start of each line. A tab after the last listed stop needs an explicit rule if the input can continue far to the right.

printf 'a\tb\tc\td\n' | expand --tabs=4,10,20

Expected output:

a   b       c         d

The first tab reaches column 4, the second reaches column 10, and the third reaches column 20. The fourth tab has no later explicit stop, so this short example is best used to illustrate the listed positions rather than as a general layout policy.

To define a repeating size after the final explicit stop, prefix that size with a slash. For example, --tabs=4,10,20/8 specifies stops at 4, 10 and 20, then continues with eight-character spacing after the final stop.

A plus sign changes the reference for the remaining stops. In --tabs=4,10,+8, the remaining spacing is relative to the last explicit stop rather than restarting from the first column. Use this only when you have a concrete column layout to test, and verify it with visible output. A malformed or unsuitable tab specification is an error, so the command will report a diagnostic and return a non-zero status.

Keep indentation tabs when appropriate

-i, or --initial, means "do not convert tabs after non-blanks". In practice, tabs before the first non-blank character are converted, while later tabs are left alone. This can help when a file mixes indentation with tab-separated content, but check that direction carefully before relying on it.

printf '\titem\tready\ntext\tvalue\n' | expand --initial

The first line has its leading tab expanded, but the tab after item remains. The second line also keeps the tab after text, because that tab follows non-blank text. Display the result with escaped control characters so the distinction is visible:

        item\tready$
text\tvalue$

Here \t is the escaped display of a tab, and the dollar signs mark line endings. This option is not a general-purpose way to preserve source formatting. It only changes which tabs are eligible based on what precedes them on each line. If the output is going into a format that forbids tabs, omit --initial and check the result instead.

Replace a file only after checking it

Once you have inspected the output, write to a temporary file in the same directory and then replace the original deliberately. The first command below is ordinary user work. The final move needs write permission on the directory, so use an elevated shell only if that directory is not writable by your account.

tmp_file=$(mktemp notes.txt.XXXXXX)
expand --tabs=8 notes.txt > "$tmp_file"
cmp -s notes.txt "$tmp_file" && echo 'No byte changes' || echo 'Converted output differs'
mv -- "$tmp_file" notes.txt

The temporary file is created beside the input, which keeps the final rename on the same filesystem. If expand fails, do not run mv; remove the temporary file with rm -- "$tmp_file". If the output is wrong after the move, recover from your backup or version-control history. expand cannot reconstruct whether a run of spaces originally came from spaces or tabs.

Common traps and checks

  • Seeing no difference. A file may already contain spaces, or your terminal may hide the distinction. Use sed -n 'l', a hex viewer, or a comparison against the source.
  • Assuming eight spaces per tab. The default is eight-character tab stops, so a tab at column 3 expands to five spaces, not eight.
  • Overwriting the input by accident. Redirect to a different path first. Keep a backup or use version control before replacing an important file.
  • Columns still look wrong. Choose stops that match the longest preceding field. Inspect lines containing wide text, because tab expansion is calculated from each line's current column.
  • Confusing expand with unexpand. expand removes tab characters by producing spaces. The separate unexpand command goes in the other direction.

For command-line help and the installed version, run:

expand --help
expand --version

Done means

  • The input file was read without needing elevated privileges.
  • Your chosen default, regular, or irregular tab stops match the intended columns.
  • sed -n 'l' or an equivalent check shows no unwanted tab characters in the converted output.
  • The original was left intact until the output had been inspected.
  • Any replacement was deliberate, and a backup or recovery path exists.