unexpand
Convert runs of spaces back into tabs
By CMD Script Team · 4 min read · Last updated
unexpand [OPTIONS] [FILE...]Options
| Flag | Description |
|---|---|
-a | Convert all runs of spaces to tabs, not just leading (initial) whitespace |
-t | Set tab stop width (or a comma-separated list of stops), default is 8 |
--first-only | Convert only the first sequence of spaces on each line, even with -a |
Distribution compatibility
- Ubuntu
- Debian
- Fedora
- Arch
- macOS
What it does
unexpand scans its input for runs of space characters and converts them back into tab
characters, based on a configurable tab-stop width (default 8). By default it only
converts leading whitespace (indentation); the -a flag extends conversion to space runs
anywhere on the line. It's the exact inverse of expand, useful when a workflow or tool
(like make) specifically requires literal tabs.
Beginner examples
unexpand file.txt— convert leading spaces back to tabs using 8-space stopsunexpand -a file.txt— convert all space runs, not just leading indentationunexpand -t 4 file.txt— convert using 4-space tab stopsunexpand file.txt > file_tabs.txt— save the converted output to a new file
unexpand -t 4 spaced_code.py > tabbed_code.py
Advanced examples
- Restore literal tabs in a Makefile whose recipe lines got space-indented by an editor:
unexpand -t 8 Makefile > Makefile.fixed && mv Makefile.fixed Makefile - Convert column-aligned space-padded text back into tab-separated form:
unexpand -a --first-only report.txt - Normalize a project from space-indentation to tab-indentation as part of a style
migration:
for f in src/*.c; do unexpand -t 4 "$f" > "$f.tmp" && mv "$f.tmp" "$f"; done - Round-trip check with
expandto confirm no content was lost:unexpand -a file.txt | expand -t 8 | diff - file.txt
unexpand -a data_spaces.txt > data_tabs.tsv
Common mistakes
- Redirecting output back onto the same input file (
unexpand file.txt > file.txt), which truncates the file beforeunexpandcan read it — always write to a temp file first. - Forgetting
-aand being confused when spaces in the middle of a line (like padding between table columns) don't get converted — default behavior only touches leading whitespace. - Assuming
unexpandandexpandare perfectly symmetric for arbitrary text — a run of spaces shorter than a full tab stop won't convert to a tab, since that would change the visual alignment. - Using the wrong
-twidth, resulting in tabs that render at a different visual width than the original spaces did in your editor.
Tips
- Use
-aspecifically for tabular data where columns are space-padded and you want a genuinely tab-separated file. - Verify the round trip:
unexpand file | expandshould reproduce the original spacing if the tab width matches what was used to create it. - Check
make-related "missing separator" errors first for accidental space-for-tab conversion —cat -A Makefile | headreveals whether recipe lines start with^I(tab) or spaces.
Best practices
- Match the
-twidth to whatever tab width the destination tool or style guide expects (makerecipes conventionally use a single tab regardless of visual width). - Never overwrite the source file directly via shell redirection; write to a new file and move it into place after confirming correctness.
- Use
-adeliberately and sparingly — converting all space runs can unexpectedly touch spacing inside strings or comments in source code, not just structural indentation.
Try it yourself
A simulated shell with a sample home directory — experiment freely, nothing leaves your browser. Type help to list supported commands.
Real-world use cases
- Fixing a Makefile that fails with
*** missing separatorbecause an editor replaced leading tabs in recipe lines with spaces. - Converting a space-indented codebase back to tabs to comply with a team's
.editorconfigor linter rule. - Compacting large space-padded tabular text exports into a smaller tab-separated format before storage or transfer.
Common interview questions
- What is the relationship between
expandandunexpand? They're inverses:expandconverts tabs to spaces,unexpandconverts runs of spaces back to tabs, both based on a configurable tab-stop width. - Why does
unexpandonly convert leading whitespace by default? Because converting arbitrary inline space runs could unintentionally alter the exact visual alignment or content of a line; leading indentation is the safe, common case, so-ais required to opt into converting everywhere. - Why would a Makefile need
unexpand?makerequires recipe lines under a target to start with a literal tab character; if an editor auto-converts tabs to spaces,unexpand -t 8restores the required literal tabs.
Frequently Asked Questions
Why would I convert spaces back to tabs?
Tab-based indentation produces smaller files and matches some style guides (Makefiles, for instance, require literal tabs for recipe lines). unexpand converts space-indented text back into tabs to satisfy those requirements or personal style preferences.
How is unexpand different from expand?
expand converts tabs to spaces; unexpand does the exact opposite, converting runs of spaces into the equivalent tabs. By default unexpand only converts leading whitespace; use -a to convert space runs anywhere on the line.
Why does a Makefile break with 'missing separator' errors?
make requires recipe lines to start with a literal tab character, not spaces. If an editor converted your tabs to spaces, run unexpand -t 8 on the file (or fix the specific lines) to restore literal tabs before the leading whitespace on recipe lines.
Cheat sheet
Download a quick-reference cheat sheet for unexpand.