I have a large number of files that are indented in a certain way and want to convert them to a different style of indentation (they are indented with 4 spaces and I want them to use 2 spaces). How can I can automatically adjust the indentation for a bulk set of files (preferably given a list of files, since only those with a certain extension have this tab size).

I'm using Windows but have access to Linux machines and have Cygwin installed.

For what it's worth, I have very clean and consistent indentation. It's not quite as simple as replacing 4 spaces with 2, though, since we only want leading spaces to be replaced.

  • What is 'a large number'? – Jan Doggen Jan 6 '15 at 10:12
  • @JanDoggen, not actually that large, maybe a hundred. Small enough any automated method should work fine but too large to do manually. – Kat Jan 6 '15 at 21:41
  • That's why I asked: for a hundred files (assuming it is a once-only operation) your text editor may be good enough if it has a keyboard macro (or repeat function) and can select all + outdent. – Jan Doggen Jan 6 '15 at 22:14

The following one-liner:

perl -ne '$_ =~ s|^((    )+)|"  " x (length($1)/4)|eg; print $_' < test.txt

Replaces 4-space indent with 2-space indents.

(You can verify by replacing " " with "-+" to see the generated pattern)

Now, we can create a bash file, let's call it indent-changer.sh:

while read filename; do
    if ! [[ -r "$filename" ]]; then
        echo "Skipping '$filename' because not readable"
    if perl -ne '$_ =~ s|^((    )+)|"  " x (length($1)/4)|eg; print $_' < "$filename" > "$tempfile"; then
        mv "$filename" "$filename".orig
        mv "$tempfile" "$filename"
        echo "Success processing '$filename'"
        echo "Failure processing '$filename'"
done < "$1"

Dump the list of files to be processed into a file, and execute the above script. Original file still exist with the suffix .orig appended. So, for example:

find . -type f -iname "*.txt" > files-to-process.lst
# Verify or edit the .lst file as needed
./indent-changer.sh files-to-process.lst > processing.log

You can check processing.log for failures easily, by doing egrep -v '^Success' processing.log.

PS: I tested the one-liner (but not the bash script) on my Cygwin installation; I don't remember if perl is part of original installation, or added afterwards. But I think it's part of original installation.

Testing the "-+" pattern with the following file:

THis is a test file
    With indentation
        more indentation
        plus    internal spaces
        indent again
        another    internal space example
          two spaces after two indents

Results in:

THis is a test file
-+With indentation
-+-+more indentation
-+-+plus    internal spaces
-+-+indent again
-+-+another    internal space example
-+-+  two spaces after two indents

Edit 2: Here's a more generic version of the Perl one-liner:

perl -ne '$f="    ";$t="  ";$_=~s|^(($f)+)|$t x (length($1)/length($f))|eg; print $_' < test.txt

With this version, simply edit the definitions for $f and $t as needed.

| improve this answer | |
  • 1
    A work of art! Thank you :) – micahblu Jul 31 '19 at 17:56

The simplest way I can think of using unexpand and expand, depending on the system you're using, the following command(works in Arch Linux) may or may not work(this command in OS X and Arch Linux has different argument set. Refer your own man unexpand and man expand for detailed usage.),

unexpand  -t 4 --first-only [your_file] > [temp]
expand -i -t 2 [temp] > [output]

What first command does is replace all leading 4-space indentation in [your_file] into tabs, result is saved as [temp]. The second command replace all leading tabs in [temp] into 2-space groups, output is [output].

Of course you can also pipe it into a shorter form

unexpand --first-only -t 4 [your_file] | expand -i -t 2 > [output]

To change indentation for a large number of files, you can write a small script file, example.sh

OLD_LENGTH=4        # old indentation length
NEW_LENGTH=2        # new indentation length
for f in $FILES; do
  unexpand --first-only -t $OLD_LENGTH f | expand -i -t $NEW_LENGTH > f

By invoking

sh ./example.sh

You will change the indentation of all files satisfying the pattern of /path/to/*.[your_suffix].

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.