Linux online course

Convert Tabs to Spaces with expand in Linux

Learn how to replace tab characters with spaces in Linux using expand, control tab stops, verify whitespace, and safely replace files.

A tab character and a space character are both whitespace, but they do not behave the same way. A tab character is a horizontal control character, commonly written as \t, that advances text to a tab stop. A space character is a literal blank character with a fixed width in a monospaced display.

The expand command converts tabs to literal spaces. This is useful when a file must contain spaces, or when another tool displays tabs inconsistently. It is also useful for preparing plain text for systems that expect fixed spacing.

How tabs differ from spaces

A tab does not inherently mean “four spaces” or “eight spaces.” It means “move forward to the next configured tab stop.” A tab stop is a column position where tabbed text can align.

For example, with repeated four-column tab stops at columns 4, 8, 12, and so on:

  • A tab beginning at column 1 may insert four spaces.
  • A tab beginning at column 3 may insert two spaces to reach column 4.
  • A tab beginning at column 5 may insert four spaces to reach column 8.

Therefore, expanding a tab according to tab stops is different from replacing every tab with one space. The -t 1 option performs the latter operation, while settings such as -t 4 preserve four-column tab-stop behavior.

What the expand command does

expand is a standard Linux and Unix utility for replacing tab characters with spaces in text. It reads either named files or standard input and writes the converted text to standard output.

expand input.txt

With this command, expand reads input.txt and prints the converted content in the terminal. It does not modify input.txt directly.

It can also read standard input. Standard input is the stream a command receives when data is piped to it or when no input file is supplied:

printf 'name\tvalue\n' | expand -t 1

The converted result is printed to standard output. Use shell redirection to save that output.

Basic tab conversion

Use the default tab stops

To convert a file using the command's default tab-stop behavior, run:

expand input.txt > output.txt

On typical Linux implementations, the default uses tab stops every eight columns. The original file remains unchanged, while output.txt receives the converted text.

Inspect the destination before replacing the source:

cat -te output.txt

When the conversion is correct, you can decide whether to keep both files or use a safe replacement workflow.

Replace every tab with one space

Use -t 1 when the requirement is exactly one literal space for every tab character:

expand -t 1 input.txt > output.txt

This does not preserve column alignment. For example, two tabs become two spaces regardless of where they occur. That behavior is appropriate for some text-processing tasks, but not for every table or source file.

The long option is equivalent:

expand --tabs=1 input.txt > output.txt

Control the tab width with -t or --tabs

The -t or --tabs option sets tab-stop positions. With one numeric value, the value is used as a repeated tab width.

OptionMeaningExampleResult
-t 1Use one-column repeated stopsexpand -t 1 input.txtEach tab becomes one space
-t 4Use four-column repeated stopsexpand -t 4 input.txtAdvance to the next multiple-of-four column
-t 8Use eight-column repeated stopsexpand -t 8 input.txtAdvance to the next multiple-of-eight column
--tabs=1Long form of the one-space settingexpand --tabs=1 input.txtEach tab becomes one space
No -t optionUse the default tab-stop behaviorexpand input.txtUsually uses stops every eight columns

For a four-column setting, the number of inserted spaces depends on the current column:

Starting columnTab settingSpaces insertedExplanation
Column 1-t 44The next stop is column 5 when columns are counted from the first character position.
Column 3-t 42The tab reaches the next four-column boundary.
Column 5-t 44The next boundary is four columns away.
Any column-t 11Every tab is replaced with exactly one space.

The exact column numbering convention is less important than the rule: with normal tab stops, a tab advances to the next stop, so its replacement length can vary according to the text before it.

Input and output patterns

Read one named file

expand input.txt > output.txt

This reads one file and writes a separate converted file.

Process piped input

printf 'name\tvalue\n' | expand -t 1

Because no input filename is supplied, expand reads standard input. Without a final redirection, the result appears in the terminal:

printf 'name\tvalue\n' | expand -t 1 > output.txt

Process multiple files

expand -t 4 first.txt second.txt > combined.txt

When multiple files are supplied, their converted contents are written consecutively to the same standard-output stream. The result is a combined output file; file boundaries are not automatically recorded. If each source needs its own destination, process them separately, for example:

for file in *.txt; do
  expand -t 4 "$file" > "$file.expanded"
done

Quote filenames such as "$file" so spaces and shell metacharacters in names are handled safely.

Verify that tabs were removed

Whitespace can look aligned even when tabs are still present. Use a representation that makes invisible characters visible.

Use cat -te

cat -te input.txt

GNU cat -te commonly displays a tab as ^I. Line endings may also be shown with a $ marker. Ordinary spaces remain visually blank, so compare the source and destination:

cat -te input.txt
cat -te output.txt

The converted output should no longer contain ^I markers.

Use sed -n l

sed -n l input.txt
sed -n l output.txt

This prints a visible, escaped representation of each line. A tab is commonly represented as \t in this output, depending on the implementation and locale.

Many editors also have a setting to display whitespace. Use that mode when available, but do not rely only on visual alignment: tabs and spaces can appear identical in a normal editor view.

Safely replace the original file

Do not redirect directly back to the input path:

expand -t 1 input.txt > input.txt

The shell opens the redirection target before expand starts reading. Opening the same path for writing can truncate the file, leaving expand with an empty or incomplete input.

Write to a temporary file, check the result, and then move it into place:

expand -t 1 input.txt > input.txt.tmp && mv input.txt.tmp input.txt

The && operator runs mv only when expand exits successfully. For important files, preserve a backup first:

cp -- input.txt input.txt.bak
expand -t 1 input.txt > input.txt.tmp && mv input.txt.tmp input.txt

Verify the temporary or replaced file before deleting the backup. A temporary filename should not already be used by another process, and you should ensure that the destination has enough free space.

ApproachCommand patternSafe for source fileReason
Separate output fileexpand input.txt > output.txtYesThe source and destination paths differ.
Temporary file followed by mvexpand input.txt > input.txt.tmp && mv input.txt.tmp input.txtYes, when verified and controlledThe source is read before the completed temporary result replaces it.
Direct redirection to the input filenameexpand input.txt > input.txtNoThe shell can truncate the input before expand reads it.

Scope and limitations

  • expand is intended for text input. Do not use it on binary files, because changing byte sequences can corrupt them.
  • It changes tab characters wherever they occur, including leading indentation and tabs embedded between words.
  • In source code, indentation may be part of a style convention, and some languages or tools interpret whitespace specially.
  • Makefile recipe lines conventionally require tabs. Converting them to spaces can make the Makefile fail.
  • In TSV data, tabs are field separators. Replacing them with spaces changes the data structure and can break imports.
  • Before conversion, determine whether the tabs are merely presentation whitespace or meaningful content.

Troubleshooting

The text no longer lines up

If you used -t 1, every tab became one space, so tabular alignment was not preserved. Use a suitable width such as -t 4 or -t 8, or confirm that one-space replacement is really required.

The source file is empty or damaged

The likely cause is redirecting output to the same filename as the input. Restore the original or a backup, then write to a different temporary or destination path before renaming it.

Tabs still seem to be present

You may be viewing the wrong file, or your viewer may render tabs and spaces alike. Run cat -te output.txt or sed -n l output.txt and look for tab markers such as ^I or \t.

A Makefile or code file stops working

Tabs may be syntactically required, especially on Makefile recipe lines. Restore the required tabs and avoid applying expand blindly to files whose whitespace has semantic meaning.

Delimited data no longer imports correctly

A TSV file uses tab characters as field separators. Keep it unchanged, or deliberately convert it to a format such as CSV using a tool that handles quoting and embedded delimiters correctly.

The reverse operation: unexpand

unexpand is the companion utility. It can replace suitable sequences of spaces with tabs:

unexpand output.txt > restored-tabs.txt

This is not necessarily a lossless reversal of every expand operation. Once tabs have become spaces, the original tab boundaries may be impossible to determine, especially after using -t 1 or when spaces already existed in the source.

Quick reference

# Default tab stops
expand input.txt > output.txt

# One literal space per tab
expand -t 1 input.txt > output.txt
expand --tabs=1 input.txt > output.txt

# Repeated four- or eight-column stops
expand -t 4 input.txt > output.txt
expand -t 8 input.txt > output.txt

# Convert piped input
printf 'name\tvalue\n' | expand -t 1

# Inspect invisible whitespace
cat -te input.txt
sed -n l input.txt

# Safely replace a file
expand -t 1 input.txt > input.txt.tmp && mv input.txt.tmp input.txt

# Attempt a related reverse conversion
unexpand output.txt > restored-tabs.txt

For related command-line techniques, see Show the Full Path Of Shell Commands and the Linux command-line topics.