7.16. Modifying a File in Place with the -i Switch
7.16.1. Problem
You need to modify a file in place from
the command line, and you're too lazy[15] for the file manipulation of
Recipe 7.15.
[15]Lazy-as-virtue,
not lazy-as-sin.
7.16.2. Solution
Use the -i and -p
switches to Perl. Write your program on the command
line:
% perl -i.orig -p -e 'FILTER COMMAND' file1 file2 file3 ...
or use the switches in programs:
#!/usr/bin/perl -i.orig -p
# filter commands go here
7.16.3. Discussion
The -i command-line switch modifies
each file in place. It creates a temporary file as in the previous
recipe, but Perl takes care of the tedious file manipulation for you.
Use it with -p (explained in Recipe 7.14) to turn:
while (<>) {
if ($ARGV ne $oldargv) { # are we at the next file?
rename($ARGV, $ARGV . ".orig");
open(ARGVOUT, ">", $ARGV); # plus error check
select(ARGVOUT);
$oldargv = $ARGV;
}
s/DATE/localtime/e;
}
continue{
print;
}
select (STDOUT); # restore default output
into:
% perl -pi.orig -e 's/DATE/localtime/e'
The -i switch takes care of making a
backup (say -i instead of
-i.orig to discard the original file contents
instead of backing them up), and -p
makes Perl loop over filenames given on the command line (or
STDIN if no files were given).The preceding one-liner would turn a file containing the following:
Dear Sir/Madam/Ravenous Beast,
As of DATE, our records show your account
is overdue. Please settle by the end of the month.
Yours in cheerful usury,
--A. Moneylender
into:
Dear Sir/Madam/Ravenous Beast,
As of Sat Apr 25 12:28:33 1998, our records show your account
is overdue. Please settle by the end of the month.
Yours in cheerful usury,
--A. Moneylender
This switch makes in-place translators a lot easier to write and to
read. For instance, this changes isolated instances of
"hisvar" to "hervar" in all C,
C++, and yacc files:
% perl -i.old -pe 's{\bhisvar\b}{hervar}g' *.[Cchy]
Turn on and off the -i behavior with
the special variable $^I. Set
@ARGV, and then use <> as
you would with -i on the command
line:
# set up to iterate over the *.c files in the current directory,
# editing in place and saving the old file with a .orig extension
local $^I = ".orig"; # emulate -i.orig
local @ARGV = glob("*.c"); # initialize list of files
while (<>) {
if ($. = = 1) {
print "This line should appear at the top of each file\n";
}
s/\b(p)earl\b/${1}erl/ig; # Correct typos, preserving case
print;
} continue {close ARGV if eof}
Beware that creating a backup file under a particular name when that
name already exists clobbers the version previously backed
up.
7.16.4. See Also
perlrun(1), and the "Switches" section of
Chapter 19 of Programming Perl; the
$^I and $. variables in
perlvar(1), and in Chapter 28 of
Programming Perl; the ..
operator in the "Range Operator" sections of
perlop(1) and Chapter 3 of
Programming Perl