Quang Thinh Ha
Quang Thinh Ha

Reputation: 241

Is there a way to change all tabs to spaces for all files in a directory using bash?

I have just inherited a new C++ project where plenty of files are indented with tabs while more recent ones are programmed with spaces. Now I want to change them all to using spaces only. Instead of going into every files and using EMacs auto-indent function, is there a more efficient way I could achieve it with bash?

Upvotes: 2

Views: 1128

Answers (3)

rkta
rkta

Reputation: 4579

Using any solution using find one has to think about all possible filename extensions, looking at the GCC manual there are plenty of them.

Ack has a very good file selection option, just pass --cc as parameter to only match C++ files.

So, replace all tabs in all C++ files including header files use:

ack --cpp -l --print0 . | xargs -0 -n1 sed -i -e 's/\t/    /' 

ack will find all C++ files recursively in the current directory and pass only the file names to sed, which replaces all tabs with four spaces.

Upvotes: 1

Dennis Williamson
Dennis Williamson

Reputation: 360335

This will traverse the project files recursively and expand the tabs to spaces. Since I'm sure you're using source control, you can easily revert if something goes awry.

find /path/to/project -type f -name '*.cpp' -exec expand --initial {} +

The default is eight spaces, but you can use --tabs=4 or whatever value you want.

The --initial option ignores tabs which follow non blanks.

Upvotes: 3

landru27
landru27

Reputation: 1702

I have two answers. First, to answer your specific question, I'd use perl or similar, like so:

replace-tab-indents.pl

#!/usr/bin/perl

while ($line = <STDIN>) {
    while ($line =~ /^ *\t/) {
        $line =~ s/^( *)\t/$1    /;
    }

    print(STDOUT $line);
}

This deals with only TABs at the start of the line. This is crucial to your use case, because you don't want to replace other TABs; e.g., those that are part of some static string in the code.

Use this inside of a shell script such as:

for FILE in `find . -name *.cpp`; do
    mv -i "$FILE" "$FILE.bak"
    cat "$FILE.bak" | replace-tab-indents.pl > "$FILE"
done

The use of the -name option on find limits you to just your source files, avoiding the problems that you correctly are wary of related to replacing all TABs in all files -- in many non-source files, the "TAB" byte is crucial to the data that comprises that file.

My second answer is to just change them as you go, instead of all at once.

This way you can employ emacs (or vim, or any modern editor, really), which will be a more tested, tried-and-true, robust approach.

The files compile just fine with TABs, so until you need to edit a given file, the presence of the TAB indentation does not affect you; at exactly the point in time when it affects you (i.e., the first time you edit it), you can use your editor to re-indent with spaces. In short, my advice here is to accomplish this just-in-time / as-you-go, not all-at-once.

Said differently, the compiler doesn't care about the indentation, only you do; and the only time you will care is when you actually edit a given source file; so, there is no empirical benefit from re-indenting your source files en mass.

Upvotes: 1

Related Questions