[linux] How can I convert spaces to tabs in Vim or Linux?

I've looked over several questions on Stack Overflow for how to convert spaces to tabs without finding what I need. There seem to be more questions about how to convert tabs to spaces, but I'm trying to do the opposite.

In Vim I've tried :retab and :retab! without luck, but I believe those are actually for going from tabs to spaces anyways.

I tried both expand and unexpand at the command prompt without any luck.

Here is the file in question:

http://gdata-python-client.googlecode.com/hg-history/a9ed9edefd61a0ba0e18c43e448472051821003a/samples/docs/docs_v3_example.py

How can I convert leading spaces to tabs using either Vim or the shell?

This question is related to linux vim tabs spaces

The answer is


Linux: with unexpand (and expand)

Here is a very good solution: https://stackoverflow.com/a/11094620/1115187, mostly because it uses *nix-utilities:

  1. unexpand — spaces -> tabs
  2. expand — tabs -> spaces

Linux: custom script

My original answer

Bash snippet for replacing 4-spaces indentation (there are two {4} in script) with tabs in all .py files in the ./app folder (recursively):

find ./app -iname '*.py' -type f \
    -exec awk -i inplace \
    '{ match($0, /^(( {4})*)(.*?)$/, arr); gsub(/ {4}/, "\t", arr[1]) }; { print arr[1] arr[3] }' {} \; 

It doesn't modify 4-spaces in the middle or at the end.

Was tested under Ubuntu 16.0x and Linux Mint 18


To use Vim to retab a set of files (e.g. all the *.ts files in a directory hierarchy) from say 2 spaces to 4 spaces you can try this from the command line:

find . -name '*.ts' -print0 | xargs -0 -n1 vim -e '+set ts=2 noet | retab! | set ts=4 et | retab | wq'

What this is doing is using find to pass all the matching files to xargs (the -print0 option on find works with the -0 option to xargs in order to handle files w/ spaces in the name).

xargs runs vim in ex mode (-e) on each file executing the given ex command which is actually several commands, to change the existing leading spaces to tabs, resetting the tab stop and changing the tabs back to spaces and finally saving and exiting.

Running in ex mode prevents this: Vim: Warning: Input is not from a terminal for each file.


Simple Python Script:

import os

SOURCE_ROOT = "ROOT DIRECTORY - WILL CONVERT ALL UNDERNEATH"

for root, dirs, files in os.walk(SOURCE_ROOT):
    for f in files:
        fpath = os.path.join(root,f)
        assert os.path.exists(fpath)
        data = open(fpath, "r").read()
        data = data.replace("    ", "\t")
        outfile = open(fpath, "w")
        outfile.write(data)
        outfile.close()

:%s/\(^\s*\)\@<=    /\t/g

Translation: Search for every instance of 4 consecutive spaces (after the = character), but only if the entire line up to that point is whitespace (this uses the zero-width look-behind assertion, \@<=). Replace each found instance with a tab character.


Changes all spaces to tab :%s/\s/\t/g


1 - If you have spaces and want tabs.

First, you need to decide how many spaces will have a single tab. That said, suppose you have lines with leading 4 spaces, or 8... Than you realize you probably want a tab to be 4 spaces. Now with that info, you do:

:set ts=4
:set noet
:%retab!

There is a problem here! This sequence of commands will look for all your text, not only spaces in the begin of the line. That mean a string like: "Hey,?this????is?4?spaces" will become "Hey,?this?is?4?spaces", but its not! its a tab!.

To settle this little problem I recomend a search, instead of retab.

:%s/^\(^I*\)????/\1^I/g

This search will look in the whole file for any lines starting with whatever number of tabs, followed by 4 spaces, and substitute it for whatever number of tabs it found plus one.

This, unfortunately, will not run at once!

At first, the file will have lines starting with spaces. The search will then convert only the first 4 spaces to a tab, and let the following...

You need to repeat the command. How many times? Until you get a pattern not found. I cannot think of a way to automatize the process yet. But if you do:

`10@:`

You are probably done. This command repeats the last search/replace for 10 times. Its not likely your program will have so many indents. If it has, just repeat again @@.

Now, just to complete the answer. I know you asked for the opposite, but you never know when you need to undo things.

2 - You have tabs and want spaces.

First, decide how many spaces you want your tabs to be converted to. Lets say you want each tab to be 2 spaces. You then do:

:set ts=2
:set et
:%retab!

This would have the same problem with strings. But as its better programming style to not use hard tabs inside strings, you actually are doing a good thing here. If you really need a tab inside a string, use \t.


If you have GNU coreutils installed, consider %!unexpand --first-only or for 4-space tabs, consider %!unexpand -t 4 --first-only (--first-only is present just in case you were accidentally invoking unexpand with --all).

Note that this will only replace the spaces preceding the prescribed tab stops, not the spaces that follow them; you will see no visual difference in vim unless you display tabs more literally; for example, my ~/.vimrc contains set list listchars=tab:?? (I suspect this is why you thought unexpand didn't work).


In my case, I had multiple spaces(fields were separated by one or more space) that I wanted to replace with a tab. The following did it:

:% s/\s\+/\t/g

Examples related to linux

grep's at sign caught as whitespace How to prevent Google Colab from disconnecting? "E: Unable to locate package python-pip" on Ubuntu 18.04 How to upgrade Python version to 3.7? Install Qt on Ubuntu Get first line of a shell command's output Cannot connect to the Docker daemon at unix:/var/run/docker.sock. Is the docker daemon running? Run bash command on jenkins pipeline How to uninstall an older PHP version from centOS7 How to update-alternatives to Python 3 without breaking apt?

Examples related to vim

Why does using from __future__ import print_function breaks Python2-style print? How to run vi on docker container? How can I install MacVim on OS X? Find and replace strings in vim on multiple lines Running Python code in Vim How do I set the default font size in Vim? Move cursor to end of file in vim Set encoding and fileencoding to utf-8 in Vim How to select all and copy in vim? Why I've got no crontab entry on OS X when using vim?

Examples related to tabs

Visual Studio Code - Convert spaces to tabs Sublime Text 3, convert spaces to tabs Bootstrap 3: Keep selected tab on page refresh Change tab bar tint color on iOS 7 Stacked Tabs in Bootstrap 3 C++ printing spaces or tabs given a user input integer Open link in new tab or window How to make a new line or tab in <string> XML (eclipse/android)? Find Active Tab using jQuery and Twitter Bootstrap How to make the tab character 4 spaces instead of 8 spaces in nano?

Examples related to spaces

Visual Studio Code - Convert spaces to tabs Removing spaces from a variable input using PowerShell 4.0 Sublime Text 3, convert spaces to tabs Read input numbers separated by spaces How do I convert a list into a string with spaces in Python? How to copy directories with spaces in the name How can I convert tabs to spaces in every file of a directory? How can I convert spaces to tabs in Vim or Linux? Adding whitespace in Java Converting a sentence string to a string array of words in Java