6

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.

3
  • What is 'a large number'?
    – Jan Doggen
    Commented Jan 6, 2015 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
    Commented Jan 6, 2015 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
    Commented Jan 6, 2015 at 22:14

2 Answers 2

4

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:

#!/bin/bash
while read filename; do
    if ! [[ -r "$filename" ]]; then
        echo "Skipping '$filename' because not readable"
        continue
    fi
    tempfile="$(mktemp)"
    if perl -ne '$_ =~ s|^((    )+)|"  " x (length($1)/4)|eg; print $_' < "$filename" > "$tempfile"; then
        mv "$filename" "$filename".orig
        mv "$tempfile" "$filename"
        echo "Success processing '$filename'"
    else
        echo "Failure processing '$filename'"
    fi
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
    outdent
        indent again
        another    internal space example
          two spaces after two indents
        end
    end
end

Results in:

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

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.

1
  • 1
    A work of art! Thank you :)
    – micahblu
    Commented Jul 31, 2019 at 17:56
9

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

FILES=/path/to/*.[your_suffix]
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
done

By invoking

sh ./example.sh

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .