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