-i edits in place¶
Level: 201 · before you point a one-liner at a file you care about
One line: -i replaces each input file with whatever the loop prints — with -p that is every line, with -n only the lines you print, and with print STDOUT nothing at all, which leaves the file empty; -i.bak keeps the original, and it behaves the same on macOS and Linux, which sed -i does not.
Measured¶
The script works on a scratch copy of demo/orders.txt, so the lesson folder is never edited:
Verified output of in_place_sh.sh — regenerated by tools/run_examples.py, never hand-typed.
$ perl -i.bak -pe 's/Warsaw/Warszawa/' orders.txt
$ ls
orders.txt
orders.txt.bak
$ cat orders.txt
1001 alice Kraków 12.50
1002 bob Warszawa 7.25
1003 alice Gdańsk 30.00
1004 carol Warszawa 4.75
$ cat orders.txt.bak
1001 alice Kraków 12.50
1002 bob Warsaw 7.25
1003 alice Gdańsk 30.00
1004 carol Warsaw 4.75
$ perl -MO=Deparse -i.bak -pe 's/Warsaw/Warszawa/' 2>/dev/null
BEGIN { $^I = ".bak"; }
LINE: while (defined($_ = readline ARGV)) {
s/Warsaw/Warszawa/;
}
continue {
die "-p destination: $!\n" unless print $_;
}
$ perl -i -ne 'print unless /bob/' orders.txt
$ ls
orders.txt
orders.txt.bak
$ cat orders.txt
1001 alice Kraków 12.50
1003 alice Gdańsk 30.00
1004 carol Warszawa 4.75
$ mkdir originals && perl -i'originals/*' -pe 's/alice/Alice/' orders.txt
$ ls originals
orders.txt
$ cat orders.txt
1001 Alice Kraków 12.50
1003 Alice Gdańsk 30.00
1004 carol Warszawa 4.75
$ perl -i -ne 'print STDOUT "read: $_"' orders.txt
read: 1001 Alice Kraków 12.50
read: 1003 Alice Gdańsk 30.00
read: 1004 carol Warszawa 4.75
$ test -s orders.txt || echo 'orders.txt is now empty'
orders.txt is now empty
What -i changes¶
Deparse shows that -i.bak adds one line, BEGIN { $^I = ".bak"; }, and nothing to the loop. $^I is what changes how ARGV behaves: while it is set, each file named on the command line is read as usual, and the loop's output — every print that does not name a filehandle — becomes that file's new contents.
Read the four runs with that in mind:
-i.bak -pe—-pprints every line, so the file is rewritten with the substitution applied, andorders.txt.bakholds the original.-i -ne 'print unless /bob/'—-nprints only what you print, so bob's line is deleted. With no extension after-i, no backup is made:lsstill shows only the first run's.bak.-i'originals/*'— a*in the extension stands for the file name, so the backup isoriginals/orders.txt. The directory has to exist first.-i -ne 'print STDOUT ...'— every line went to the terminal and none to the default output, soorders.txtis now empty. No warning; it did what it was told.
Before you press Enter¶
Run it with -i.bak first, compare the backup with the result, and delete the backup once you trust it. A one-liner that forgets -p, or prints to a named handle, empties every file it was given — all of them, before you see the first.
sed -i is two different options¶
GNU sed takes -i with an optional suffix attached; BSD sed, on a Mac, needs an argument even when it is empty, sed -i ''. A script written on one fails on the other. perl -i -pe is the same command on both, and CI runs this lesson on both. The encodings library measures the sed side: sed matches patterns, not bytes ↗.
If you are coming from another language¶
- Python. The standard library's
fileinputmodule has an in-place mode —fileinput.input(files, inplace=True, backup='.bak')— in whichprintwrites into the file being read. Same idea, same way to empty a file. - The shell.
cmd < file > filetruncatesfilebeforecmdreads it.-iexists so you never write that.
See also¶
-nand-pare a loop — the loop whose output-icaptures- perlrun,
-i↗ — the backup-name rules