Editing files in-place with sponge

TL;DR:

$ apt-get install moreutils
$ man sponge

Editing existing files in-place with regular UNIX-tools like sed, awk, or — like in my case — expand (which converts tabs to spaces), can be a hassle.

A Non-solution

The obvious first attempt is to run it like this:

me:~/tmp$ cat > bad.py
def foo():
    print "I'm using tabs!"
me:~/tmp$ cat bad.py | expand - | sed -e s/tabs/spaces/ > bad.py
me:~/tmp$ cat bad.py
me:~/tmp$

Aaaaah, it's empty!!!!11eleventyone

The reason: Since all these commands run in parallel, you're reading from the file at the same time that you're already writing to it. In the normal case, you'll get an empty file. (You lose your data!)

Teh solution

This is where the sponge tool from the Debian moreutils package comes into play: sponge file gobbles up all input piped into it, and only after that is done, it will write the input to the given file.

me:~/tmp$ cat > bad.py
def foo():
    print "I'm using tabs!"
me:~/tmp$ cat bad.py | expand - | sed -e s/tabs/spaces/ | sponge bad.py
me:~/tmp$ cat bad.py
def foo():
        print "I'm using spaces!"

It works, w00t!

Now, to convert all source code in the current directory:

$ for i in *.scm; do expand "$i" | sponge "$i"; done

There's an even more elaborate writeup on these techniques at http://backreference.org/2011/01/29/in-place-editing-of-files/.