Linux convert tabs to spaces; spaces to tabs in shell scripts (Bash)

Converting between tab- and space-separated text is a routine pipeline task: log fields, CSV-ish exports, and Makefile recipes (where a leading tab still matters). Turning tabs into spaces is usually expand; turning repeated spaces into tabs is unexpand. For tab literals in Bash, use $'\t' or printf '\t' instead of pasting a literal tab character.

Tested on Ubuntu 25.04, GNU coreutils expand / unexpand 9.5, Bash 5.2.37.


Linux convert tabs to spaces (expand)

expand reads text and replaces tab characters with spaces so that tab stops line up at fixed columns (default often 8; override with -t).

bash
printf '%s\n' $'name\tvalue\nfoo\tbar' | expand -t 8 | cat -A
text
name    value$
foo     bar$

cat -A shows ^I for a tab and plain spaces otherwise—handy when verifying conversion in a script:

bash
expand -t 4 input.txt > output.txt

Pipe instead of files when you only need a one-off conversion step in a larger pipeline.


Convert spaces to tabs (unexpand, sed, tr)

unexpand (GNU coreutils)

unexpand merges leading spaces (and optionally other blanks) into tabs.

bash
printf '    line with 4 leading spaces\n' | unexpand -t 4 --first-only | cat -A
text
^Iline with 4 leading spaces$

Flags worth reading in man unexpand: -a (all blanks on the line, not only leading), tab width -t.

One-line sed / tr for simple space runs

Collapse multiple spaces and turn separators into tabs when the line is predictable:

bash
printf '%s\n' 'col1  col2   col3' | tr -s ' ' | sed 's/ /\t/g' | cat -A
text
col1^Icol2^Icol3$

For heavier field logic, awk is often clearer—see awk examples.


Tab in bash ($'\t', printf, heredocs)

Tab literals in Bash:

bash
printf 'A%sB\n' $'\t' | cat -A
printf 'A\tB\n' | cat -A
text
A^IB$
A^IB$

In a <<EOF heredoc, a real tab character is a tab; many style guides prefer <<'EOF' when you do not want expansions, which also freezes tabs literally. Avoid invisible tabs in conditionals—they are easy to miscount in review.


Aligned columns: prefer explicit widths over fragile tab math

Older scripts sometimes measured string length with wc -c and expr to decide how many spaces to print. That breaks on multi-byte characters and is harder to read than printf field widths:

bash
printf '%-12s : %s\n' "col1" "col2" "col3" "col4"
text
col1         : col2
col3         : col4

If you truly need tab stops between dynamic fields, build the line with $'\t' between segments, then run expand -t n once for consistent viewing.


Summary

Use expand to convert tabs to spaces (tune stops with -t). Converting runs of spaces to tabs leans on unexpand or small sed / tr filters when the layout is simple. Tab literals in Bash are $'\t' or printf '\t'; verify with cat -A. For human-readable tables in scripts, fixed-width printf is usually more robust than hand-tuned tab padding. GNU details live in man expand and man unexpand.

Further reading: GNU expand manual, GNU unexpand manual. For timed loops that sometimes format output, see bash while loop until a specific time.

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)