Have you ever needed to change the text in a

bunch of files quickly? You could open each file

and then do a search-and-replace. This process

can be time-consuming, tedious, and error-prone.

So instead, let's automate this task with

Perl.

The

Code

#!/usr/local/bin/perl

#

# Replaces a string within multiple files

# specified on the command line

$mv = '/bin/mv';

$op = shift || die("Usage: $0 perlexpr [filenames]

");

if (!@ARGV) {

@ARGV = <STDIN>;

chop(@ARGV);

}

foreach $file (@ARGV) {

if (!-f $file) {

print "Skipping non-regular file: $file

";

next;

}

if (-B $file) {

print "Skipping binary file: $file

";

next;

}

$outfile = "/usr/tmp/$file.$$";

open(FILE, $file) ||

die("Couldn't open $file: $!

");

undef $/;

$_ = <FILE>;

close(FILE);

if (eval $op) {

open(OFILE, "> $outfile") ||

die("Couldn't open $outfile: $!

");

print OFILE;

close(OFILE);

system($mv, '-f', $file, "$file.bak");

system($mv, '-f', $outfile, $file);

print "File changed: $file

";

}

else {

print "No change to file: $file

";

}

}

exit(0);

The Explanation

Save the above code into a file called

replace. Make sure that the

permissions are set correctly so that you can

execute the script. Also check to make sure that

your Perl interpreter is in /usr/local/bin. If

Perl is somewhere else, you'll need to change the

first line to point to where Perl is installed on

your system. Finally, check to make sure that

your mv executable is in /bin

which it should be. If not, you'll need to change

line 6, $mv = '/bin/mv'; to point

to the correct location of the executable.

To use the script you would type:

replace perlexpr

[files]

where perlexpr is the

substitution operator, i.e., s///. You can

actually pass any Perl expression through to

perlexpr allowing you to do more

complex text substitutions. If you decide to

that, however, make sure that your expression

leaves the block with a non-zero value if you want

the change to take place. It's easiest to make

this value the number of replacements that

actually occurred. The files

argument is a list of filesname that you want to

change. You can leave this argument out and the

script will take a list of names from STDIN.

The script works by reading in each text file

that is passed to it and evaluating the Perl

expression that is supplied on the command-line.

If the expression evaluates to TRUE, it creates a

temporary file containing the changes, makes a

backup of the original file

(filename.bak), and then copies the

changed file over top of the original file. Also,

since the script reads the entire file into a

single scalar variable, you'll probably want to

use the /g modifier to any substitution commands

that you pass in; this will ensure that multiple

occurrences are replaced and not just the first

one.

The Examples

  1. Your company has changed its name from 'Get

    Rich Quick Corp.' to 'Suckers Are Us' and you need

    to change all your Web pages to reflect this.

    replace 's/Get Rich Quick

    Corp\./Suckers Are Us/g' *

  2. The new guy in your office has started

    using .htm in all of his links while the files are

    actually end in .html.

    replace 's/\.htm\"/\.html\"/g'

    *.htm

    You can then use your

    rename

    script
    to change the file suffixes from .htm

    to .html.

    Note: This assumes that your

    links end with a double quotation mark and that

    .htm" does not appear anywhere but in a

    link.

  3. You want to highlight all references to my

    name in your files.

    replace 's!Dean Mah!<strong>Dean

    Mah</strong>!g' *

The standard warning applies here when using

regular expressions, make your expressions as

specific as possible, otherwise they are bound to

match something that you didn't want it to. To be

safe, you should compare the original file with

the changed file using something like

diff to make sure that only the

changes that you wanted occurred.

If you have any problems or questions about

this script, you can e-mail them to me at:

dmah@vox.org.