Ever had to change the URL in 50 files? What do you do?

1
vi *.html

(You should know by now I’d use Emacs.)

Or whip up a Bash script with sed?

Since dinosaurs roamed the Earth, I’ve been using Perl’s In Place Editing feature -i.

1
perl -p -i.orig -e 's/bar/baz/' file

Let’s break it down, shall we? -e is the code to execute, you can do something as simple as:

1
perl -e 'print "Hello\n"'

-p changes the meaning of -e. Instead of executing the code once, it loops over the input files and applies the code to each line. In Ruby it would look like:

1
2
3
while gets
# -e code runs here
end

In Perl you could implement the classic Cloud to Butt with:

1
perl -p -e 's/cloud/butt/ig' file

This will output the contents of the file with all occurrences of cloud replaced with butt.

That brings us to -i. -i says edit the file in place. Instead of printing the modified contents of the file, it replaces the file with the new version. (Note to the Pedantic, -i isn’t actually original to Perl, it’s one of the ideas Perl took from sed. It just happens to be where I learned it.) This can be a little scary, the sed docs say:

If [no argument] is given, no backup will be saved. It is not recommended to give a zero-length extension when in-place editing files, as you risk corruption or partial content in situations where disk space is exhausted, etc.

So, -i takes an optional argument which is used to name a backup copy the original file. Thus…

1
perl -p -i.orig -e 's/bar/baz/g' file

Copies file into file.orig and saves a new version with bar replaced by baz. If you give it multiple files, each one is backed-up and then processed.

1
perl -p -i.orig -e 's/bar/baz/g' file0 file1 ... fileN

Oh, and multiple -e arguments are fine if you terminate each one with a “;”.

1
perl -p -i.orig -e 's/bar/baz/g;' -e s/foo/teh fooz/g' file0 file1 ... fileN

which is the same as:

1
perl -p -i.orig -e 's/bar/baz/g; s/foo/teh fooz/g' file0 file1 ... fileN

Why on Earth am I telling you this on a primary Ruby blog? Because everything I just told you works in Ruby as well. The only difference is the code:

1
ruby -p -i.orig -e 'gsub(/bar/,"baz")' file0 file1 ... fileN

And the fact that Ruby doesn’t need semicolons if you use multiple -e arguments. (It does, of course, if you combine them in one -e argument.)

1
ruby -p -i.orig -e 'gsub(/bar/,"baz")' -e 'gsub(/foo/,"teh foo")' file0 file1 ... fileN

What’s it good for? The above URL change, of course (.ly is sooo last year).

1
ruby -p -i.orig -e 'gsub('http://example.ly','http://example.io)' *.html

The links will be changed and the original files backed up as .html.orig

And don’t forget that you can use pattern matching with #gsub. Consider the directive to add decimal places to all prices in a file:

1
ruby -p -i.orig -e 'gsub(/\$(\d+)/, "\\0.00")' prices.txt

So next time you find yourself opening a bunch of files to do a search and replace, instead throw Ruby at it and get it done in no time.

Comments