In-place file editing
In the examples presented so far, the output from Perl was displayed on the terminal or redirected to another file. This chapter will discuss how to write back the changes to the input files using the -i
command line option. This option can be configured to make changes to the input files with or without creating a backup of original contents. When backups are needed, the original filename can get a prefix or a suffix or both. And the backups can be placed in the same directory or some other directory as needed.
The example_files directory has all the files used in the examples.
With backup
You can use the -i
option to write back the changes to the input file instead of displaying the output on terminal. When an extension is provided as an argument to -i
, the original contents of the input file gets preserved as per the extension given. For example, if the input file is ip.txt
and -i.orig
is used, the backup file will be named as ip.txt.orig
.
$ cat colors.txt
deep blue
light orange
blue delight
# no output on the terminal as -i option is used
# space is NOT allowed between -i and the extension
$ perl -i.bkp -pe 's/blue/-green-/' colors.txt
# changes are written back to 'colors.txt'
$ cat colors.txt
deep -green-
light orange
-green- delight
# original file is preserved in 'colors.txt.bkp'
$ cat colors.txt.bkp
deep blue
light orange
blue delight
Without backup
Sometimes backups are not desirable. In such cases, you can use the -i
option without an argument. Be careful though, as changes made cannot be undone. It is recommended to test the command with sample inputs before applying the -i
option on the actual file. You could also use the option with backup, compare the differences with a diff
program and then delete the backup.
$ cat fruits.txt
banana
papaya
mango
$ perl -i -pe 's/(..)\1/\U$&/g' fruits.txt
$ cat fruits.txt
bANANa
PAPAya
mango
Multiple files
Multiple input files are treated individually and the changes are written back to respective files.
$ cat t1.txt
have a nice day
bad morning
what a pleasant evening
$ cat t2.txt
worse than ever
too bad
$ perl -i.bkp -pe 's/bad/good/' t1.txt t2.txt
$ ls t?.*
t1.txt t1.txt.bkp t2.txt t2.txt.bkp
$ cat t1.txt
have a nice day
good morning
what a pleasant evening
$ cat t2.txt
worse than ever
too good
Prefix backup name
A *
character in the argument to the -i
option is special. It will get replaced with the input filename. This is helpful if you need to use a prefix instead of a suffix for the backup filename. Or any other combination that may be needed.
$ ls *colors.txt*
colors.txt colors.txt.bkp
# single quotes is used here as * is a special shell character
$ perl -i'bkp.*' -pe 's/-green-/yellow/' colors.txt
$ ls *colors.txt*
bkp.colors.txt colors.txt colors.txt.bkp
Place backups in a different directory
The *
trick can also be used to place the backups in another directory instead of the parent directory of input files. The backup directory should already exist for this to work.
$ mkdir backups
$ perl -i'backups/*' -pe 's/good/nice/' t1.txt t2.txt
$ ls backups/
t1.txt t2.txt
Gory details of in-place editing
For more details about the -i
option, see:
- Effective Perl Programming: In-place editing gets safer in v5.28
- perldoc: -i option — documentation and underlying code
- perldoc faq: Why does Perl let me delete read-only files? Why does -i clobber protected files? Isn't this a bug in Perl?
Summary
This chapter discussed about the -i
option which is useful when you need to edit a file in-place. This is particularly useful in automation scripts. But, do ensure that you have tested the Perl command before applying to actual files if you need to use this option without creating backups.
Exercises
The exercises directory has all the files used in this section.
1) For the input file text.txt
, replace all occurrences of in
with an
and write back the changes to text.txt
itself. The original contents should get saved to text.txt.orig
$ cat text.txt
can ran want plant
tin fin fit mine line
##### add your solution here
$ cat text.txt
can ran want plant
tan fan fit mane lane
$ cat text.txt.orig
can ran want plant
tin fin fit mine line
2) For the input file text.txt
, replace all occurrences of an
with in
and write back the changes to text.txt
itself. Do not create backups for this exercise. Note that you should have solved the previous exercise before starting this one.
$ cat text.txt
can ran want plant
tan fan fit mane lane
##### add your solution here
$ cat text.txt
cin rin wint plint
tin fin fit mine line
$ diff text.txt text.txt.orig
1c1
< cin rin wint plint
---
> can ran want plant
3) For the input file copyright.txt
, replace copyright: 2018
with copyright: 2020
and write back the changes to copyright.txt
itself. The original contents should get saved to 2018_copyright.txt.bkp
$ cat copyright.txt
bla bla 2015 bla
blah 2018 blah
bla bla bla
copyright: 2018
##### add your solution here
$ cat copyright.txt
bla bla 2015 bla
blah 2018 blah
bla bla bla
copyright: 2020
$ cat 2018_copyright.txt.bkp
bla bla 2015 bla
blah 2018 blah
bla bla bla
copyright: 2018
4) In the code sample shown below, two files are created by redirecting the output of the echo
command. Then a Perl command is used to edit b1.txt
in-place as well as create a backup named bkp.b1.txt
. Will the Perl command work as expected? If not, why?
$ echo '2 apples' > b1.txt
$ echo '5 bananas' > -ibkp.txt
$ perl -ibkp.* -pe 's/2/two/' b1.txt
5) For the input file pets.txt
, remove the first occurrence of I like
from each line and write back the changes to pets.txt
itself. The original contents should get saved with the same filename inside the bkp
directory. Assume that you do not know whether bkp
exists or not in the current working directory.
$ cat pets.txt
I like cats
I like parrots
I like dogs
##### add your solution here
$ cat pets.txt
cats
parrots
dogs
$ cat bkp/pets.txt
I like cats
I like parrots
I like dogs